From b0d231c9273ad12770e8a49fa45fda2015815852 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Feb 2026 18:54:49 +0300 Subject: [PATCH 01/73] optimize collisions calculations (remove the stupid) --- src/maths/aabb.hpp | 12 ++- src/objects/Entities.cpp | 2 +- src/physics/PhysicsSolver.cpp | 127 ++++++++++++++----------- src/voxels/Chunks.cpp | 40 ++++---- src/voxels/Chunks.hpp | 13 ++- src/voxels/GlobalChunks.cpp | 4 +- src/voxels/GlobalChunks.hpp | 2 +- src/voxels/blocks_agent.hpp | 35 ++++--- src/world/generator/WorldGenerator.cpp | 2 +- 9 files changed, 134 insertions(+), 103 deletions(-) diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index 471441951..d5dc9493b 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -38,6 +38,14 @@ struct AABB { return AABB(a + pos, b + pos); } + AABB operator+(const glm::vec3& offset) const { + return translated(offset); + } + + AABB operator-(const glm::vec3& offset) const { + return translated(-offset); + } + /// @brief Multiply AABB size from center inline void scale(const glm::vec3 mul) { glm::vec3 center = (a + b) * 0.5f; @@ -91,14 +99,14 @@ struct AABB { addPoint(matrix * glm::vec4(pb.x, pa.y, pb.z, 1.0f)); } - inline bool intersect(const AABB& aabb) { + inline bool intersects(const AABB& aabb) const { return ( a.x <= aabb.b.x && b.x >= aabb.a.x && a.y <= aabb.b.y && b.y >= aabb.a.y && a.z <= aabb.b.z && b.z >= aabb.a.z ); } - inline bool intersect(const AABB& aabb, float margin) { + inline bool intersects(const AABB& aabb, float margin) const { return ( a.x <= aabb.b.x + margin && b.x >= aabb.a.x - margin && a.y <= aabb.b.y + margin && b.y >= aabb.a.y - margin && diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index cbb507c89..a41ab7798 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -450,7 +450,7 @@ void Entities::render( bool Entities::hasBlockingInside(AABB aabb) { auto view = registry->view(); for (auto [entity, eid, body] : view.each()) { - if (eid.def.blocking && aabb.intersect(body.hitbox.getAABB(), -0.05f)) { + if (eid.def.blocking && aabb.intersects(body.hitbox.getAABB(), -0.05f)) { return true; } } diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index e20bafacd..1a33952f7 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -5,8 +5,8 @@ #include "voxels/Block.hpp" #include "voxels/GlobalChunks.hpp" #include "voxels/voxel.hpp" +#include "debug/Logger.hpp" -#include #include #define GLM_ENABLE_EXPERIMENTAL #include @@ -14,8 +14,12 @@ inline const float E = 0.03f; inline const float MAX_FIX = 0.1f; +static debug::Logger logger("physics-solver"); + PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {} +static int samples_count = 0; + void PhysicsSolver::step( const GlobalChunks& chunks, Hitbox& hitbox, @@ -23,9 +27,9 @@ void PhysicsSolver::step( uint substeps, entityid_t entity ) { + samples_count = 0; float dt = delta / static_cast(substeps); float linearDamping = hitbox.linearDamping * hitbox.friction; - float s = 2.0f/BLOCK_AABB_GRID; auto half = hitbox.getHalfSize(); glm::vec3& pos = hitbox.position; @@ -53,11 +57,12 @@ void PhysicsSolver::step( if (hitbox.crouching && hitbox.grounded){ float y = (pos.y-half.y-E); hitbox.grounded = false; - for (int ix = 0; ix <= (half.x-E)*2/s; ix++) { - float x = (px-half.x+E) + ix * s; - for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ - float z = (pos.z-half.z+E) + iz * s; - if (chunks.isObstacleAt(x,y,z)){ + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + float x = (px-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ + float z = (pos.z-half.z+E) + iz; + samples_count++; + if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ hitbox.grounded = true; break; } @@ -68,11 +73,12 @@ void PhysicsSolver::step( vel.z = 0.0f; } hitbox.grounded = false; - for (int ix = 0; ix <= (half.x-E)*2/s; ix++) { - float x = (pos.x-half.x+E) + ix * s; - for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ - float z = (pz-half.z+E) + iz * s; - if (chunks.isObstacleAt(x,y,z)){ + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + float x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ + float z = (pz-half.z+E) + iz; + samples_count++; + if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ hitbox.grounded = true; break; } @@ -103,7 +109,7 @@ void PhysicsSolver::step( bool triggered = false; switch (sensor.type) { case SensorType::AABB: - triggered = aabb.intersect(sensor.calculated.aabb); + triggered = aabb.intersects(sensor.calculated.aabb); break; case SensorType::RADIUS: triggered = glm::distance2( @@ -118,21 +124,23 @@ void PhysicsSolver::step( sensor.nextEntered.insert(entity); } } + logger.info() << "total samples: " << samples_count; } static float calc_step_height( const GlobalChunks& chunks, const glm::vec3& pos, const glm::vec3& half, - float stepHeight, - float s + float stepHeight ) { + AABB aabb(pos - half, pos + half); if (stepHeight > 0.0f) { - for (int ix = 0; ix <= (half.x-E)*2/s; ix++) { - float x = (pos.x-half.x+E) + ix * s; - for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { - float z = (pos.z-half.z+E) + iz * s; - if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) { + for (int ix = 0; ix <= glm::ceil((half.x-E) * 2); ix++) { + float x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { + float z = (pos.z-half.z+E) + iz; + samples_count++; + if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z, aabb)) { return 0.0f; } } @@ -147,20 +155,26 @@ static bool calc_collision_neg( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight, - float s + float stepHeight ) { if (vel[nx] >= 0.0f) { return false; } glm::vec3 offset(0.0f, stepHeight, 0.0f); - for (int iy = 0; iy <= std::max(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) { + for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { glm::vec3 coord; - coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s; - for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++){ - coord[nz] = (pos[nz]-half[nz]+E) + iz * s; + coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; + for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++){ + coord[nz] = (pos[nz]-half[nz]+E) + iz; coord[nx] = (pos[nx]-half[nx]-E); - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { + samples_count++; + + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale[nz] = 1.0f - E * 2.0f; + boxAABB.scale(scale); + + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; if (newx - pos[nx] <= E) { @@ -179,23 +193,29 @@ static void calc_collision_pos( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight, - float s + float stepHeight ) { if (vel[nx] <= 0.0f) { return; } glm::vec3 offset(0.0f, stepHeight, 0.0f); - for (int iy = 0; iy <= std::max(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) { + for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { glm::vec3 coord; - coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s; - for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) { - coord[nz] = (pos[nz]-half[nz]+E) + iz * s; + coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; + for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++) { + coord[nz] = (pos[nz]-half[nz]+E) + iz; coord[nx] = (pos[nx]+half[nx]+E); - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { + samples_count++; + + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale[nz] = 1.0f - E * 2.0f; + boxAABB.scale(scale); + + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; - if (newx - pos[nx] <= E) { + if (newx - pos[nx] >= -E) { pos[nx] = newx; } return; @@ -212,30 +232,28 @@ void PhysicsSolver::colisionCalc( const glm::vec3 half, float stepHeight ) { - // step size (smaller - more accurate, but slower) // TODO: GET RID OF THIS - float s = 2.0f/BLOCK_AABB_GRID; - - stepHeight = calc_step_height(chunks, pos, half, stepHeight, s); + stepHeight = calc_step_height(chunks, pos, half, stepHeight); const AABB* aabb; - calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight, s); - calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight, s); + calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight); + calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight); - calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight, s); - calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight, s); + calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight); + calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight); - if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f, s)) { + if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f)) { hitbox.grounded = true; } if (stepHeight > 0.0 && vel.y <= 0.0f){ - for (int ix = 0; ix <= (half.x-E)*2/s; ix++) { - float x = (pos.x-half.x+E) + ix * s; - for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { - float z = (pos.z-half.z+E) + iz * s; + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + float x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { + float z = (pos.z-half.z+E) + iz; float y = (pos.y-half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z))){ + samples_count++; + if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) { @@ -247,12 +265,13 @@ void PhysicsSolver::colisionCalc( } } if (vel.y > 0.0f){ - for (int ix = 0; ix <= (half.x-E)*2/s; ix++) { - float x = (pos.x-half.x+E) + ix * s; - for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { - float z = (pos.z-half.z+E) + iz * s; + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + float x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { + float z = (pos.z-half.z+E) + iz; float y = (pos.y+half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z))){ + samples_count++; + if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; if (std::abs(newy-pos.y) <= MAX_FIX) { diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index dc3f6153f..2a85b38bd 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -1,11 +1,5 @@ #include "Chunks.hpp" -#include - -#include -#include -#include - #include "data/StructLayout.hpp" #include "coders/byte_utils.hpp" #include "content/Content.hpp" @@ -20,6 +14,11 @@ #include "VoxelsVolume.hpp" #include "blocks_agent.hpp" +#include +#include +#include +#include + Chunks::Chunks( int32_t w, int32_t d, @@ -67,20 +66,21 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) const { } } const auto& def = indices.blocks.require(v->id); - if (def.obstacle) { - glm::ivec3 offset {}; - if (v->state.segment) { - glm::ivec3 point(ix, iy, iz); - offset = seekOrigin(point, def, v->state) - point; - } - const auto& boxes = - def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; - for (const auto& hitbox : boxes) { - if (hitbox.contains( - {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} - )) { - return &hitbox; - } + if (!def.obstacle) { + return nullptr; + } + glm::ivec3 offset {}; + if (v->state.segment) { + glm::ivec3 point(ix, iy, iz); + offset = seekOrigin(point, def, v->state) - point; + } + const auto& boxes = + def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; + for (const auto& hitbox : boxes) { + if (hitbox.contains( + {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} + )) { + return &hitbox; } } return nullptr; diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 4fe24f289..9fdebff1c 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -1,17 +1,16 @@ #pragma once -#include - -#include -#include -#include -#include - #include "typedefs.hpp" #include "voxel.hpp" #include "constants.hpp" #include "util/AreaMap2D.hpp" +#include +#include +#include +#include +#include + class VoxelRenderer; struct AABB; diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 032ae17e2..11e9d5b5e 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -214,6 +214,6 @@ void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } -const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const { - return blocks_agent::is_obstacle_at(*this, x, y, z); +const AABB* GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const { + return blocks_agent::is_obstacle_at(*this, x, y, z, aabb); } diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index 7120658c2..83b6c17bc 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -57,7 +57,7 @@ public: void putChunk(std::shared_ptr chunk); - const AABB* isObstacleAt(float x, float y, float z) const; + const AABB* isObstacleAt(float x, float y, float z, const AABB& aabb) const; inline Chunk* getChunk(int cx, int cz) const { const auto& found = chunksMap.find(keyfrom(cx, cz)); diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 9ffac7f93..2dc80d14b 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -461,7 +461,7 @@ void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false void get_voxels(const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight=false); template -inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) { +inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z, const AABB& aabb) { int ix = std::floor(x); int iy = std::floor(y); int iz = std::floor(z); @@ -475,23 +475,28 @@ inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float } } const auto& def = chunks.getContentIndices().blocks.require(v->id); - if (def.obstacle) { - glm::ivec3 offset {}; - if (v->state.segment) { - glm::ivec3 point(ix, iy, iz); - offset = seek_origin(chunks, point, def, v->state) - point; - } - const auto& boxes = - def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; - for (const auto& hitbox : boxes) { - if (hitbox.contains( - {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} - )) { - return &hitbox; - } + if (!def.obstacle) { + return nullptr; + } + glm::ivec3 offset {}; + if (v->state.segment) { + glm::ivec3 point(ix, iy, iz); + offset = seek_origin(chunks, point, def, v->state) - point; + } + const auto& boxes = + def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; + + for (const auto& hitbox : boxes) { + if (hitbox.intersects(aabb - glm::ivec3(ix, iy, iz))) { + return &hitbox; } } return nullptr; } +template +inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) { + return is_obstacle_at(chunks, x, y, z, AABB({x, y, z}, {x + 1, y + 1, z + 1})); +} + } // blocks_agent diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 1f9a2241d..260a28e3c 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -173,7 +173,7 @@ void WorldGenerator::placeStructure( } auto& otherPrototype = *found->second; auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); - if (chunkAABB.intersect(aabb)) { + if (chunkAABB.intersects(aabb)) { otherPrototype.placements.emplace_back( priority, StructurePlacement { From 5ea8df0743ccd986e4395111130664fe5ca89433 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Feb 2026 18:57:20 +0300 Subject: [PATCH 02/73] remove debug logging --- src/physics/PhysicsSolver.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 1a33952f7..d718948a3 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -18,8 +18,6 @@ static debug::Logger logger("physics-solver"); PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {} -static int samples_count = 0; - void PhysicsSolver::step( const GlobalChunks& chunks, Hitbox& hitbox, @@ -27,7 +25,6 @@ void PhysicsSolver::step( uint substeps, entityid_t entity ) { - samples_count = 0; float dt = delta / static_cast(substeps); float linearDamping = hitbox.linearDamping * hitbox.friction; @@ -61,7 +58,6 @@ void PhysicsSolver::step( float x = (px-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ float z = (pos.z-half.z+E) + iz; - samples_count++; if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ hitbox.grounded = true; break; @@ -77,7 +73,6 @@ void PhysicsSolver::step( float x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ float z = (pz-half.z+E) + iz; - samples_count++; if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ hitbox.grounded = true; break; @@ -124,7 +119,6 @@ void PhysicsSolver::step( sensor.nextEntered.insert(entity); } } - logger.info() << "total samples: " << samples_count; } static float calc_step_height( @@ -139,7 +133,6 @@ static float calc_step_height( float x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z+E) + iz; - samples_count++; if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z, aabb)) { return 0.0f; } @@ -167,7 +160,6 @@ static bool calc_collision_neg( for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++){ coord[nz] = (pos[nz]-half[nz]+E) + iz; coord[nx] = (pos[nx]-half[nx]-E); - samples_count++; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -205,7 +197,6 @@ static void calc_collision_pos( for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++) { coord[nz] = (pos[nz]-half[nz]+E) + iz; coord[nx] = (pos[nx]+half[nx]+E); - samples_count++; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -252,7 +243,6 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z+E) + iz; float y = (pos.y-half.y+E); - samples_count++; if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; @@ -270,7 +260,6 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z+E) + iz; float y = (pos.y+half.y+E); - samples_count++; if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; From 1ea54d10bc1ea7c423a64c38aa8bf8c5c2225698 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Feb 2026 21:18:21 +0300 Subject: [PATCH 03/73] fixes (part 1) --- src/physics/PhysicsSolver.cpp | 47 ++++++++++++++++++++++++----------- src/voxels/blocks_agent.hpp | 15 ++++++----- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index d718948a3..7ba00e61a 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -32,7 +32,7 @@ void PhysicsSolver::step( glm::vec3& pos = hitbox.position; glm::vec3& vel = hitbox.velocity; float gravityScale = hitbox.gravityScale; - + bool prevGrounded = hitbox.grounded; hitbox.grounded = false; for (uint i = 0; i < substeps; i++) { @@ -52,13 +52,18 @@ void PhysicsSolver::step( } if (hitbox.crouching && hitbox.grounded){ + AABB aabb; + float y = (pos.y-half.y-E); hitbox.grounded = false; + + aabb = AABB(pos - half, pos + half); + aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { float x = (px-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ float z = (pos.z-half.z+E) + iz; - if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ + if (chunks.isObstacleAt(x,y,z, aabb)){ hitbox.grounded = true; break; } @@ -69,11 +74,14 @@ void PhysicsSolver::step( vel.z = 0.0f; } hitbox.grounded = false; + + aabb = AABB(pos - half, pos + half); + aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { float x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ float z = (pz-half.z+E) + iz; - if (chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half))){ + if (chunks.isObstacleAt(x,y,z, aabb)){ hitbox.grounded = true; break; } @@ -127,12 +135,14 @@ static float calc_step_height( const glm::vec3& half, float stepHeight ) { - AABB aabb(pos - half, pos + half); + AABB aabb(-half, +half); + aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); + aabb = aabb + pos + glm::vec3(0.0f, stepHeight, 0.0f); if (stepHeight > 0.0f) { for (int ix = 0; ix <= glm::ceil((half.x-E) * 2); ix++) { - float x = (pos.x-half.x+E) + ix; + float x = (pos.x-half.x) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z+E) + iz; + float z = (pos.z-half.z) + iz; if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z, aabb)) { return 0.0f; } @@ -148,12 +158,13 @@ static bool calc_collision_neg( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight + float stepHeight, + float margin = 1.0f ) { if (vel[nx] >= 0.0f) { return false; } - glm::vec3 offset(0.0f, stepHeight, 0.0f); + glm::vec3 offset(0.0f, (stepHeight + E) * margin, 0.0f); for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { glm::vec3 coord; coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; @@ -165,11 +176,12 @@ static bool calc_collision_neg( glm::vec3 scale(1.0f); scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); + boxAABB = boxAABB + offset; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; - float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; - if (newx - pos[nx] <= E) { + float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E * margin; + if (newx - pos[nx] <= E * margin) { pos[nx] = newx; } return true; @@ -190,7 +202,7 @@ static void calc_collision_pos( if (vel[nx] <= 0.0f) { return; } - glm::vec3 offset(0.0f, stepHeight, 0.0f); + glm::vec3 offset(0.0f, stepHeight + E * 2, 0.0f); for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { glm::vec3 coord; coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; @@ -202,6 +214,7 @@ static void calc_collision_pos( glm::vec3 scale(1.0f); scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); + boxAABB = boxAABB + offset; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; @@ -233,17 +246,21 @@ void PhysicsSolver::colisionCalc( calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight); calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight); - if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f)) { + if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f, 0.0f)) { hitbox.grounded = true; } if (stepHeight > 0.0 && vel.y <= 0.0f){ + AABB boxAABB = AABB(-half, +half); + boxAABB.scale(glm::vec3(1.0f - E, 1.0f, 1.0f - E)); + boxAABB = boxAABB.translated(pos); + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { - float x = (pos.x-half.x+E) + ix; + float x = (pos.x-half.x) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z+E) + iz; + float z = (pos.z-half.z) + iz; float y = (pos.y-half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ + if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) { diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 2dc80d14b..d1af7cc23 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -2,23 +2,22 @@ /// blocks_agent is set of templates but not a class to minimize OOP overhead. -#include "voxel.hpp" #include "Block.hpp" #include "Chunk.hpp" #include "Chunks.hpp" -#include "VoxelsVolume.hpp" -#include "GlobalChunks.hpp" #include "constants.hpp" -#include "typedefs.hpp" #include "content/Content.hpp" +#include "GlobalChunks.hpp" #include "maths/voxmaths.hpp" +#include "typedefs.hpp" +#include "voxel.hpp" +#include "VoxelsVolume.hpp" #include -#include -#include -#include -#include #include +#include +#include +#include struct AABB; From 411bfecedff23deae29a63936b06768329fd89b1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 19 Feb 2026 23:47:25 +0300 Subject: [PATCH 04/73] update PhysicsSolver.cpp --- src/physics/PhysicsSolver.cpp | 63 +++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 7ba00e61a..99ca01e40 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -40,13 +40,14 @@ void PhysicsSolver::step( float py = pos.y; float pz = pos.z; - vel += gravity * dt * gravityScale; if (hitbox.type == BodyType::DYNAMIC) { colisionCalc(chunks, hitbox, vel, pos, half, (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f); } + vel += gravity * dt * gravityScale; pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; + if (hitbox.grounded && pos.y < py) { pos.y = py; } @@ -58,7 +59,7 @@ void PhysicsSolver::step( hitbox.grounded = false; aabb = AABB(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); + aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { float x = (px-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ @@ -76,7 +77,7 @@ void PhysicsSolver::step( hitbox.grounded = false; aabb = AABB(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); + aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { float x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ @@ -158,13 +159,12 @@ static bool calc_collision_neg( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight, - float margin = 1.0f + float stepHeight ) { if (vel[nx] >= 0.0f) { return false; } - glm::vec3 offset(0.0f, (stepHeight + E) * margin, 0.0f); + glm::vec3 offset(0.0f, stepHeight + E, 0.0f); for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { glm::vec3 coord; coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; @@ -177,11 +177,12 @@ static bool calc_collision_neg( scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; + boxAABB.b.y -= stepHeight; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; - float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E * margin; - if (newx - pos[nx] <= E * margin) { + float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; + if (newx - pos[nx] <= E) { pos[nx] = newx; } return true; @@ -191,6 +192,41 @@ static bool calc_collision_neg( return false; } +static bool calc_collision_neg_y( + const GlobalChunks& chunks, + glm::vec3& pos, + glm::vec3& vel, + const glm::vec3& half +) { + if (vel.y >= 0.0f) { + return false; + } + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + glm::vec3 coord; + coord.x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ + coord.z = (pos.z-half.z+E) + iz; + coord.y = (pos.y-half.y-E); + + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale.x = 1.0f - E * 4.0f; + scale.z = 1.0f - E * 4.0f; + boxAABB.scale(scale); + + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { + vel.y = 0.0f; + float newx = std::floor(coord.y) + half.y + aabb->max().y; + if (newx - pos.y <= E) { + pos.y = newx; + } + return true; + } + } + } + return false; +} + template static void calc_collision_pos( const GlobalChunks& chunks, @@ -215,6 +251,7 @@ static void calc_collision_pos( scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; + boxAABB.b.y -= stepHeight; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { vel[nx] = 0.0f; @@ -246,13 +283,13 @@ void PhysicsSolver::colisionCalc( calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight); calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight); - if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f, 0.0f)) { + if (calc_collision_neg_y(chunks, pos, vel, half)) { hitbox.grounded = true; } if (stepHeight > 0.0 && vel.y <= 0.0f){ AABB boxAABB = AABB(-half, +half); - boxAABB.scale(glm::vec3(1.0f - E, 1.0f, 1.0f - E)); + boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); boxAABB = boxAABB.translated(pos); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { @@ -272,12 +309,16 @@ void PhysicsSolver::colisionCalc( } } if (vel.y > 0.0f){ + AABB boxAABB = AABB(-half, +half); + boxAABB.scale(glm::vec3(1.0f - E* 2, 1.0f, 1.0f - E * 2)); + boxAABB = boxAABB.translated(pos); + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { float x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z+E) + iz; float y = (pos.y+half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z, AABB(pos - half, pos + half)))){ + if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; if (std::abs(newy-pos.y) <= MAX_FIX) { From 0a34f9b76730d1d9eb9c73bf61b11b327e441bd5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 23 Feb 2026 17:34:24 +0300 Subject: [PATCH 05/73] add solid entities (wip) --- src/content/loading/EntityLoader.cpp | 1 + src/objects/Entities.cpp | 19 ++- src/objects/EntityDef.cpp | 2 + src/objects/EntityDef.hpp | 3 + src/physics/Hitbox.hpp | 1 + src/physics/PhysicsSolver.cpp | 213 ++++++++++++++------------- src/physics/PhysicsSolver.hpp | 35 +++-- 7 files changed, 158 insertions(+), 116 deletions(-) diff --git a/src/content/loading/EntityLoader.cpp b/src/content/loading/EntityLoader.cpp index 12e5313a7..b8dfa9f4e 100644 --- a/src/content/loading/EntityLoader.cpp +++ b/src/content/loading/EntityLoader.cpp @@ -88,5 +88,6 @@ template<> void ContentUnitLoader::loadUnit( root.at("skeleton-name").get(def.skeletonName); root.at("blocking").get(def.blocking); + root.at("solid").get(def.solid); root.at("step-height").get(def.stepHeight); } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index a41ab7798..53b606da8 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -278,13 +278,17 @@ void Entities::updateSensors( } void Entities::preparePhysics(float delta) { + auto& physics = *level.physics; + auto& solidHitboxes = physics.getSolidHitboxesWriteable(); + + if (sensorsTickClock.update(delta)) { auto part = sensorsTickClock.getPart(); auto parts = sensorsTickClock.getParts(); + auto& sensors = physics.getSensorsWriteable(); + auto view = registry->view(); - auto physics = level.physics.get(); - std::vector sensors; for (auto [entity, eid, transform, rigidbody] : view.each()) { if (!rigidbody.enabled) { continue; @@ -294,7 +298,16 @@ void Entities::preparePhysics(float delta) { } updateSensors(rigidbody, transform, sensors); } - physics->setSensors(std::move(sensors)); + } + + solidHitboxes.clear(); + + auto view = registry->view(); + for (auto [entity, eid, rigidbody] : view.each()) { + if (!eid.def.solid || eid.destroyFlag || !rigidbody.enabled) { + continue; + } + solidHitboxes.emplace_back(&rigidbody.hitbox); } } diff --git a/src/objects/EntityDef.cpp b/src/objects/EntityDef.cpp index 4cc858710..cab635584 100644 --- a/src/objects/EntityDef.cpp +++ b/src/objects/EntityDef.cpp @@ -9,5 +9,7 @@ void EntityDef::cloneTo(EntityDef& dst) { dst.radialSensors = radialSensors; dst.skeletonName = skeletonName; dst.blocking = blocking; + dst.solid = solid; + dst.stepHeight = stepHeight; dst.save = save; } diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index 3752f3063..ad93d445e 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -42,6 +42,9 @@ struct EntityDef { /// @brief Does entity prevent blocks setup bool blocking = true; + /// @brief Is the entity solid (blocks other entities) + bool solid = false; + /// @brief Max obstacle height that does not require jumping float stepHeight = 0.5f; diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 909a48451..2b4a1aad7 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -60,6 +60,7 @@ struct Hitbox { float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; + glm::vec3 groundVelocity {}; Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize); diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 99ca01e40..3df3fa7b8 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -5,6 +5,7 @@ #include "voxels/Block.hpp" #include "voxels/GlobalChunks.hpp" #include "voxels/voxel.hpp" +#include "objects/Entities.hpp" #include "debug/Logger.hpp" #include @@ -12,88 +13,102 @@ #include inline const float E = 0.03f; -inline const float MAX_FIX = 0.1f; static debug::Logger logger("physics-solver"); -PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {} +PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(std::move(gravity)) {} + +void PhysicsSolver::calcSubstep( + const GlobalChunks& chunks, + Hitbox& hitbox, + glm::vec3& vel, + glm::vec3& pos, + bool prevGrounded, + float dt +) { + auto initpos = pos; + auto half = hitbox.getHalfSize(); + float gravityScale = hitbox.gravityScale; + + if (hitbox.type == BodyType::DYNAMIC) { + calcCollisions( + chunks, + hitbox, + vel, + pos, + half, + (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f + ); + } + + vel += gravity * dt * gravityScale; + pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; + + if (hitbox.grounded && pos.y < initpos.y) { + pos.y = initpos.y; + } + + if (prevGrounded && !hitbox.grounded) { + vel += hitbox.groundVelocity; + hitbox.groundVelocity = {}; + } + + if (!hitbox.crouching || !hitbox.grounded) { + return; + } + + float y = (pos.y-half.y-E); + for (int axis = 0; axis <= 2; axis += 2) { + hitbox.grounded = false; + + AABB aabb(pos - half, pos + half); + aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { + float x = (initpos.x - half.x + E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){ + float z = (pos.z - half.z + E) + iz; + if (chunks.isObstacleAt(x, y, z, aabb)){ + hitbox.grounded = true; + break; + } + } + } + for (const auto& box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + if (AABB(box->position - boxhalf, box->position + boxhalf) + .intersects(AABB(pos - half, pos + half))) { + hitbox.grounded = true; + break; + } + } + if (!hitbox.grounded) { + pos[axis] = initpos[axis]; + vel[axis] = 0.0f; + } + } + hitbox.grounded = true; +} void PhysicsSolver::step( - const GlobalChunks& chunks, - Hitbox& hitbox, - float delta, - uint substeps, + const GlobalChunks& chunks, + Hitbox& hitbox, + float delta, + uint substeps, entityid_t entity ) { float dt = delta / static_cast(substeps); float linearDamping = hitbox.linearDamping * hitbox.friction; - auto half = hitbox.getHalfSize(); glm::vec3& pos = hitbox.position; glm::vec3& vel = hitbox.velocity; - float gravityScale = hitbox.gravityScale; bool prevGrounded = hitbox.grounded; hitbox.grounded = false; for (uint i = 0; i < substeps; i++) { - float px = pos.x; - float py = pos.y; - float pz = pos.z; - - if (hitbox.type == BodyType::DYNAMIC) { - colisionCalc(chunks, hitbox, vel, pos, half, - (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f); - } - - vel += gravity * dt * gravityScale; - pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; - - if (hitbox.grounded && pos.y < py) { - pos.y = py; - } - - if (hitbox.crouching && hitbox.grounded){ - AABB aabb; - - float y = (pos.y-half.y-E); - hitbox.grounded = false; - - aabb = AABB(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { - float x = (px-half.x+E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ - float z = (pos.z-half.z+E) + iz; - if (chunks.isObstacleAt(x,y,z, aabb)){ - hitbox.grounded = true; - break; - } - } - } - if (!hitbox.grounded) { - pos.z = pz; - vel.z = 0.0f; - } - hitbox.grounded = false; - - aabb = AABB(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { - float x = (pos.x-half.x+E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ - float z = (pz-half.z+E) + iz; - if (chunks.isObstacleAt(x,y,z, aabb)){ - hitbox.grounded = true; - break; - } - } - } - if (!hitbox.grounded) { - pos.x = px; - vel.x = 0.0f; - } - hitbox.grounded = true; - } + calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt); } vel.x /= 1.0f + delta * linearDamping; vel.z /= 1.0f + delta * linearDamping; @@ -194,19 +209,22 @@ static bool calc_collision_neg( static bool calc_collision_neg_y( const GlobalChunks& chunks, + const std::vector& solidHitboxes, glm::vec3& pos, glm::vec3& vel, - const glm::vec3& half + const glm::vec3& half, + glm::vec3& groundVelocty ) { if (vel.y >= 0.0f) { return false; } + groundVelocty = {}; for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { glm::vec3 coord; coord.x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ coord.z = (pos.z-half.z+E) + iz; - coord.y = (pos.y-half.y-E); + coord.y = (pos.y-half.y-E) - E; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -215,15 +233,30 @@ static bool calc_collision_neg_y( boxAABB.scale(scale); if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - vel.y = 0.0f; - float newx = std::floor(coord.y) + half.y + aabb->max().y; - if (newx - pos.y <= E) { - pos.y = newx; + float newy = std::floor(coord.y) + half.y + aabb->max().y; + if (newy > pos.y) { + vel.y = 0.0f; + pos.y = newy; } return true; } } } + for (const auto& box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + if (AABB(box->position - boxhalf, box->position + boxhalf).intersects(AABB(pos - half, pos + half))) { + vel.y = 0.0f; + float newx = box->position.y + boxhalf.y + half.y; + if (newx - pos.y <= 0.5f) { + pos.y = newx; + } + groundVelocty = box->velocity; + return true; + } + } return false; } @@ -265,8 +298,8 @@ static void calc_collision_pos( } } -void PhysicsSolver::colisionCalc( - const GlobalChunks& chunks, +void PhysicsSolver::calcCollisions( + const GlobalChunks& chunks, Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, @@ -283,7 +316,7 @@ void PhysicsSolver::colisionCalc( calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight); calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight); - if (calc_collision_neg_y(chunks, pos, vel, half)) { + if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { hitbox.grounded = true; } @@ -300,7 +333,7 @@ void PhysicsSolver::colisionCalc( if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; - if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) { + if (std::abs(newy-pos.y) <= stepHeight) { pos.y = newy; } break; @@ -321,7 +354,7 @@ void PhysicsSolver::colisionCalc( if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; - if (std::abs(newy-pos.y) <= MAX_FIX) { + if (std::abs(newy-pos.y) <= 0.0f) { pos.y = newy; } break; @@ -331,32 +364,6 @@ void PhysicsSolver::colisionCalc( } } -bool PhysicsSolver::isBlockInside(int x, int y, int z, Hitbox* hitbox) { - const glm::vec3& pos = hitbox->position; - auto half = hitbox->getHalfSize(); - return x >= floor(pos.x-half.x) && x <= floor(pos.x+half.x) && - z >= floor(pos.z-half.z) && z <= floor(pos.z+half.z) && - y >= floor(pos.y-half.y) && y <= floor(pos.y+half.y); -} - -bool PhysicsSolver::isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox) { - const float e = 0.001f; // inaccuracy - const glm::vec3& pos = hitbox->position; - auto half = hitbox->getHalfSize(); - const auto& boxes = def->rotatable - ? def->rt.hitboxes[state.rotation] - : def->hitboxes; - for (const auto& block_hitbox : boxes) { - glm::vec3 min = block_hitbox.min(); - glm::vec3 max = block_hitbox.max(); - if (min.x < pos.x+half.x-x-e && max.x > pos.x-half.x-x+e && - min.z < pos.z+half.z-z-e && max.z > pos.z-half.z-z+e && - min.y < pos.y+half.y-y-e && max.y > pos.y-half.y-y+e) - return true; - } - return false; -} - void PhysicsSolver::removeSensor(Sensor* sensor) { sensors.erase(std::remove(sensors.begin(), sensors.end(), sensor), sensors.end()); } diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index b7810cefd..052cf58e3 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -9,12 +9,11 @@ #include class Block; +class Entities; class GlobalChunks; struct Sensor; class PhysicsSolver { - glm::vec3 gravity; - std::vector sensors; public: PhysicsSolver(glm::vec3 gravity); void step( @@ -24,7 +23,22 @@ public: uint substeps, entityid_t entity ); - void colisionCalc( + + auto& getSensorsWriteable() { + return sensors; + } + + auto& getSolidHitboxesWriteable() { + return solidHitboxes; + } + + void removeSensor(Sensor* sensor); +private: + glm::vec3 gravity; + std::vector sensors; + std::vector solidHitboxes; + + void calcCollisions( const GlobalChunks& chunks, Hitbox& hitbox, glm::vec3& vel, @@ -32,12 +46,13 @@ public: const glm::vec3 half, float stepHeight ); - bool isBlockInside(int x, int y, int z, Hitbox* hitbox); - bool isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox); - void setSensors(std::vector sensors) { - this->sensors = std::move(sensors); - } - - void removeSensor(Sensor* sensor); + void calcSubstep( + const GlobalChunks& chunks, + Hitbox& hitbox, + glm::vec3& vel, + glm::vec3& pos, + bool prevGrounded, + float dt + ); }; From c8e884009bdc7b7ee8025b05cfb12284f340d0af Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 23 Feb 2026 18:44:32 +0300 Subject: [PATCH 06/73] fix extended blocks collision detection --- src/physics/Hitbox.hpp | 1 + src/physics/PhysicsSolver.cpp | 45 ++++++++++++++++++++++------------- src/voxels/GlobalChunks.cpp | 2 +- src/voxels/GlobalChunks.hpp | 3 ++- src/voxels/blocks_agent.hpp | 15 ++++++------ 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 2b4a1aad7..a5137818b 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -57,6 +57,7 @@ struct Hitbox { float friction = 1.0f; float verticalDamping = 1.0f; bool grounded = false; + bool collided[3] {}; float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 3df3fa7b8..7eba5088a 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -174,7 +174,8 @@ static bool calc_collision_neg( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight + float stepHeight, + bool collided[3] ) { if (vel[nx] >= 0.0f) { return false; @@ -185,7 +186,7 @@ static bool calc_collision_neg( coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++){ coord[nz] = (pos[nz]-half[nz]+E) + iz; - coord[nx] = (pos[nx]-half[nx]-E); + coord[nx] = (pos[nx]-half[nx]-E) - E; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -195,10 +196,11 @@ static bool calc_collision_neg( boxAABB.b.y -= stepHeight; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - vel[nx] = 0.0f; float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; - if (newx - pos[nx] <= E) { + if (pos[nx] < newx) { + vel[nx] = 0.0f; pos[nx] = newx; + collided[nx] = true; } return true; } @@ -266,7 +268,8 @@ static void calc_collision_pos( glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, - float stepHeight + float stepHeight, + bool collided[3] ) { if (vel[nx] <= 0.0f) { return; @@ -277,7 +280,7 @@ static void calc_collision_pos( coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++) { coord[nz] = (pos[nz]-half[nz]+E) + iz; - coord[nx] = (pos[nx]+half[nx]+E); + coord[nx] = (pos[nx]+half[nx]+E) + E; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -287,10 +290,11 @@ static void calc_collision_pos( boxAABB.b.y -= stepHeight; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - vel[nx] = 0.0f; float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; - if (newx - pos[nx] >= -E) { + if (pos[nx] > newx) { + vel[nx] = 0.0f; pos[nx] = newx; + collided[nx] = true; } return; } @@ -306,20 +310,29 @@ void PhysicsSolver::calcCollisions( const glm::vec3 half, float stepHeight ) { + hitbox.collided[0] = false; + hitbox.collided[1] = false; + hitbox.collided[2] = false; stepHeight = calc_step_height(chunks, pos, half, stepHeight); - const AABB* aabb; - - calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight); - calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight); + auto prevPos = pos; - calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight); - calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight); + calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight, hitbox.collided); + + calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight, hitbox.collided); if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { hitbox.grounded = true; } + for (int axis = 0; axis < 3; axis++) { + if (hitbox.collided[axis]) { + pos[axis] = prevPos[axis]; + } + } + if (stepHeight > 0.0 && vel.y <= 0.0f){ AABB boxAABB = AABB(-half, +half); boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); @@ -330,7 +343,7 @@ void PhysicsSolver::calcCollisions( for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z) + iz; float y = (pos.y-half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ + if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy-pos.y) <= stepHeight) { @@ -351,7 +364,7 @@ void PhysicsSolver::calcCollisions( for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { float z = (pos.z-half.z+E) + iz; float y = (pos.y+half.y+E); - if ((aabb = chunks.isObstacleAt(x,y,z, boxAABB))){ + if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; if (std::abs(newy-pos.y) <= 0.0f) { diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 11e9d5b5e..d24d8b26d 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -214,6 +214,6 @@ void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } -const AABB* GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const { +std::optional GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const { return blocks_agent::is_obstacle_at(*this, x, y, z, aabb); } diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index 83b6c17bc..de62cbc58 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #define GLM_ENABLE_EXPERIMENTAL @@ -57,7 +58,7 @@ public: void putChunk(std::shared_ptr chunk); - const AABB* isObstacleAt(float x, float y, float z, const AABB& aabb) const; + std::optional isObstacleAt(float x, float y, float z, const AABB& aabb) const; inline Chunk* getChunk(int cx, int cz) const { const auto& found = chunksMap.find(keyfrom(cx, cz)); diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index d1af7cc23..11470fe44 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -460,22 +460,21 @@ void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false void get_voxels(const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight=false); template -inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z, const AABB& aabb) { +inline std::optional is_obstacle_at(const Storage& chunks, float x, float y, float z, const AABB& aabb) { int ix = std::floor(x); int iy = std::floor(y); int iz = std::floor(z); voxel* v = get(chunks, ix, iy, iz); if (v == nullptr) { if (iy >= CHUNK_H) { - return nullptr; + return std::nullopt; } else { - static const AABB empty; - return ∅ + return AABB(); } } const auto& def = chunks.getContentIndices().blocks.require(v->id); if (!def.obstacle) { - return nullptr; + return std::nullopt; } glm::ivec3 offset {}; if (v->state.segment) { @@ -487,14 +486,14 @@ inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float for (const auto& hitbox : boxes) { if (hitbox.intersects(aabb - glm::ivec3(ix, iy, iz))) { - return &hitbox; + return hitbox + offset; } } - return nullptr; + return std::nullopt; } template -inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) { +inline std::optional is_obstacle_at(const Storage& chunks, float x, float y, float z) { return is_obstacle_at(chunks, x, y, z, AABB({x, y, z}, {x + 1, y + 1, z + 1})); } From ed9ceb26baca0dcb614ce5745151a0df49bf03b4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 23 Feb 2026 21:20:36 +0300 Subject: [PATCH 07/73] fix ceiling collision & feat: stepping on solid entity --- src/physics/PhysicsSolver.cpp | 56 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 7eba5088a..aebb804ac 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -193,7 +193,7 @@ static bool calc_collision_neg( scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight; + boxAABB.b.y -= stepHeight + E * 2; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; @@ -287,7 +287,7 @@ static void calc_collision_pos( scale[nz] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight; + boxAABB.b.y -= stepHeight + E * 2; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; @@ -326,6 +326,29 @@ void PhysicsSolver::calcCollisions( if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { hitbox.grounded = true; } + + if (vel.y > 0.0f){ + AABB boxAABB = AABB(-half, +half); + boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f + E * 2, 1.0f - E * 2)); + boxAABB = boxAABB.translated(pos); + + for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + float x = (pos.x-half.x+E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { + float z = (pos.z-half.z+E) + iz; + float y = (pos.y+half.y+E) + 0.5f; + if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ + float newy = std::floor(y) - half.y + aabb->min().y - E; + if (pos.y > newy) { + vel.y = 0.0f; + pos.y = newy; + hitbox.collided[1] = true; + } + break; + } + } + } + } for (int axis = 0; axis < 3; axis++) { if (hitbox.collided[axis]) { @@ -353,24 +376,17 @@ void PhysicsSolver::calcCollisions( } } } - } - if (vel.y > 0.0f){ - AABB boxAABB = AABB(-half, +half); - boxAABB.scale(glm::vec3(1.0f - E* 2, 1.0f, 1.0f - E * 2)); - boxAABB = boxAABB.translated(pos); - - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { - float x = (pos.x-half.x+E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z+E) + iz; - float y = (pos.y+half.y+E); - if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ - vel.y = 0.0f; - float newy = std::floor(y) - half.y + aabb->min().y - E; - if (std::abs(newy-pos.y) <= 0.0f) { - pos.y = newy; - } - break; + // succesfully increasing code duplication + for (const auto& box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + if (AABB(box->position - boxhalf, box->position + boxhalf).intersects(AABB(pos - half, pos + half))) { + vel.y = 0.0f; + float newx = box->position.y + boxhalf.y + half.y; + if (newx - pos.y <= 0.5f) { + pos.y = newx; } } } From b52e990e4050067654046e0defbec735bb5dee41 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Feb 2026 00:38:13 +0300 Subject: [PATCH 08/73] fix step on & move some code somewhere --- src/physics/Hitbox.hpp | 2 +- src/physics/PhysicsSolver.cpp | 405 +++++++++++++++++----------------- 2 files changed, 204 insertions(+), 203 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index a5137818b..870e24725 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -66,7 +66,7 @@ struct Hitbox { Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize); AABB getAABB() const { - return AABB(position-halfsize, position+halfsize); + return AABB(position - halfsize, position + halfsize); } glm::vec3 getHalfSize() const { diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index aebb804ac..b36f4a337 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -18,133 +18,6 @@ static debug::Logger logger("physics-solver"); PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(std::move(gravity)) {} -void PhysicsSolver::calcSubstep( - const GlobalChunks& chunks, - Hitbox& hitbox, - glm::vec3& vel, - glm::vec3& pos, - bool prevGrounded, - float dt -) { - auto initpos = pos; - auto half = hitbox.getHalfSize(); - float gravityScale = hitbox.gravityScale; - - if (hitbox.type == BodyType::DYNAMIC) { - calcCollisions( - chunks, - hitbox, - vel, - pos, - half, - (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f - ); - } - - vel += gravity * dt * gravityScale; - pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; - - if (hitbox.grounded && pos.y < initpos.y) { - pos.y = initpos.y; - } - - if (prevGrounded && !hitbox.grounded) { - vel += hitbox.groundVelocity; - hitbox.groundVelocity = {}; - } - - if (!hitbox.crouching || !hitbox.grounded) { - return; - } - - float y = (pos.y-half.y-E); - for (int axis = 0; axis <= 2; axis += 2) { - hitbox.grounded = false; - - AABB aabb(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); - for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { - float x = (initpos.x - half.x + E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){ - float z = (pos.z - half.z + E) + iz; - if (chunks.isObstacleAt(x, y, z, aabb)){ - hitbox.grounded = true; - break; - } - } - } - for (const auto& box : solidHitboxes) { - if (glm::distance2(box->position, pos) < E) { - continue; - } - auto boxhalf = box->getHalfSize(); - if (AABB(box->position - boxhalf, box->position + boxhalf) - .intersects(AABB(pos - half, pos + half))) { - hitbox.grounded = true; - break; - } - } - if (!hitbox.grounded) { - pos[axis] = initpos[axis]; - vel[axis] = 0.0f; - } - } - hitbox.grounded = true; -} - -void PhysicsSolver::step( - const GlobalChunks& chunks, - Hitbox& hitbox, - float delta, - uint substeps, - entityid_t entity -) { - float dt = delta / static_cast(substeps); - float linearDamping = hitbox.linearDamping * hitbox.friction; - - glm::vec3& pos = hitbox.position; - glm::vec3& vel = hitbox.velocity; - - bool prevGrounded = hitbox.grounded; - hitbox.grounded = false; - for (uint i = 0; i < substeps; i++) { - calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt); - } - vel.x /= 1.0f + delta * linearDamping; - vel.z /= 1.0f + delta * linearDamping; - if (hitbox.verticalDamping > 0.0f) { - vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping; - } - - AABB aabb; - aabb.a = hitbox.position - hitbox.getHalfSize(); - aabb.b = hitbox.position + hitbox.getHalfSize(); - for (size_t i = 0; i < sensors.size(); i++) { - auto& sensor = *sensors[i]; - if (sensor.entity == entity) { - continue; - } - - bool triggered = false; - switch (sensor.type) { - case SensorType::AABB: - triggered = aabb.intersects(sensor.calculated.aabb); - break; - case SensorType::RADIUS: - triggered = glm::distance2( - hitbox.position, glm::vec3(sensor.calculated.radial)) - < sensor.calculated.radial.w; - break; - } - if (triggered) { - if (sensor.prevEntered.find(entity) == sensor.prevEntered.end()) { - sensor.enterCallback(sensor.entity, sensor.index, entity); - } - sensor.nextEntered.insert(entity); - } - } -} - static float calc_step_height( const GlobalChunks& chunks, const glm::vec3& pos, @@ -168,6 +41,46 @@ static float calc_step_height( return stepHeight; } +template +static void calc_collision_pos( + const GlobalChunks& chunks, + glm::vec3& pos, + glm::vec3& vel, + const glm::vec3& half, + float stepHeight, + bool collided[3] +) { + if (vel[nx] <= 0.0f) { + return; + } + glm::vec3 offset(0.0f, stepHeight + E * 2, 0.0f); + for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { + glm::vec3 coord; + coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; + for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { + coord[nz] = (pos[nz] - half[nz] + E) + iz; + coord[nx] = (pos[nx] + half[nx] + E) + E; + + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale[nz] = 1.0f - E * 2.0f; + boxAABB.scale(scale); + boxAABB = boxAABB + offset; + boxAABB.b.y -= stepHeight + E * 2; + + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { + float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; + if (pos[nx] > newx) { + vel[nx] = 0.0f; + pos[nx] = newx; + collided[nx] = true; + } + return; + } + } + } +} + template static bool calc_collision_neg( const GlobalChunks& chunks, @@ -181,13 +94,13 @@ static bool calc_collision_neg( return false; } glm::vec3 offset(0.0f, stepHeight + E, 0.0f); - for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { + for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { glm::vec3 coord; - coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; - for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++){ - coord[nz] = (pos[nz]-half[nz]+E) + iz; - coord[nx] = (pos[nx]-half[nx]-E) - E; - + coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; + for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { + coord[nz] = (pos[nz] - half[nz] + E) + iz; + coord[nx] = (pos[nx] - half[nx] - E) - E; + auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale[nz] = 1.0f - E * 2.0f; @@ -217,6 +130,23 @@ static bool calc_collision_neg_y( const glm::vec3& half, glm::vec3& groundVelocty ) { + for (const auto& box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + if (box->getAABB().intersects(AABB(pos - half, pos + half))) { + if (vel.y < 0.0f) { + vel.y = 0.0f; + } + float newx = box->position.y + boxhalf.y + half.y; + if (newx - pos.y <= 0.5f) { + pos.y = newx; + } + groundVelocty = box->velocity; + return true; + } + } if (vel.y >= 0.0f) { return false; } @@ -244,64 +174,9 @@ static bool calc_collision_neg_y( } } } - for (const auto& box : solidHitboxes) { - if (glm::distance2(box->position, pos) < E) { - continue; - } - auto boxhalf = box->getHalfSize(); - if (AABB(box->position - boxhalf, box->position + boxhalf).intersects(AABB(pos - half, pos + half))) { - vel.y = 0.0f; - float newx = box->position.y + boxhalf.y + half.y; - if (newx - pos.y <= 0.5f) { - pos.y = newx; - } - groundVelocty = box->velocity; - return true; - } - } return false; } -template -static void calc_collision_pos( - const GlobalChunks& chunks, - glm::vec3& pos, - glm::vec3& vel, - const glm::vec3& half, - float stepHeight, - bool collided[3] -) { - if (vel[nx] <= 0.0f) { - return; - } - glm::vec3 offset(0.0f, stepHeight + E * 2, 0.0f); - for (int iy = 0; iy <= glm::ceil(((half-offset*0.5f)[ny]-E)*2); iy++) { - glm::vec3 coord; - coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy; - for (int iz = 0; iz <= glm::ceil((half[nz]-E)*2); iz++) { - coord[nz] = (pos[nz]-half[nz]+E) + iz; - coord[nx] = (pos[nx]+half[nx]+E) + E; - - auto boxAABB = AABB(pos - half, pos + half); - glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 2.0f; - boxAABB.scale(scale); - boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight + E * 2; - - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; - if (pos[nx] > newx) { - vel[nx] = 0.0f; - pos[nx] = newx; - collided[nx] = true; - } - return; - } - } - } -} - void PhysicsSolver::calcCollisions( const GlobalChunks& chunks, Hitbox& hitbox, @@ -356,43 +231,169 @@ void PhysicsSolver::calcCollisions( } } - if (stepHeight > 0.0 && vel.y <= 0.0f){ + // step on + if (stepHeight > 0.0 && vel.y <= 0.0f) { AABB boxAABB = AABB(-half, +half); - boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); + boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f - E * 2, 1.0f - E * 2)); boxAABB = boxAABB.translated(pos); - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { float x = (pos.x-half.x) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z) + iz; - float y = (pos.y-half.y+E); - if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { + float z = (pos.z - half.z) + iz; + float y = (pos.y - half.y + E) + E; + if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) { vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; - if (std::abs(newy-pos.y) <= stepHeight) { - pos.y = newy; + if (std::abs(newy - pos.y) <= stepHeight) { + pos.y = newy; } break; } } } - // succesfully increasing code duplication for (const auto& box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { continue; } auto boxhalf = box->getHalfSize(); - if (AABB(box->position - boxhalf, box->position + boxhalf).intersects(AABB(pos - half, pos + half))) { + if (box->getAABB().intersects(boxAABB)) { vel.y = 0.0f; - float newx = box->position.y + boxhalf.y + half.y; - if (newx - pos.y <= 0.5f) { - pos.y = newx; + float newy = box->position.y + boxhalf.y + half.y; + if (std::abs(newy - pos.y) <= stepHeight) { + pos.y = newy; } } } } } +void PhysicsSolver::calcSubstep( + const GlobalChunks& chunks, + Hitbox& hitbox, + glm::vec3& vel, + glm::vec3& pos, + bool prevGrounded, + float dt +) { + auto initpos = pos; + auto half = hitbox.getHalfSize(); + float gravityScale = hitbox.gravityScale; + + if (hitbox.type == BodyType::DYNAMIC) { + calcCollisions( + chunks, + hitbox, + vel, + pos, + half, + (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f + ); + } + + vel += gravity * dt * gravityScale; + pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; + + if (hitbox.grounded && pos.y < initpos.y) { + pos.y = initpos.y; + } + + // crouching + if (!hitbox.crouching || !hitbox.grounded) { + return; + } + float y = (pos.y - half.y - E); + for (int axis = 0; axis <= 2; axis += 2) { + hitbox.grounded = false; + + AABB aabb(pos - half, pos + half); + aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { + float x = (initpos.x - half.x + E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){ + float z = (pos.z - half.z + E) + iz; + if (chunks.isObstacleAt(x, y, z, aabb)){ + hitbox.grounded = true; + break; + } + } + } + for (const auto& box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + if (box->getAABB().intersects(AABB(pos - half, pos + half))) { + hitbox.grounded = true; + break; + } + } + if (!hitbox.grounded) { + pos[axis] = initpos[axis]; + vel[axis] = 0.0f; + } + } + hitbox.grounded = true; +} + +void PhysicsSolver::step( + const GlobalChunks& chunks, + Hitbox& hitbox, + float delta, + uint substeps, + entityid_t entity +) { + float dt = delta / static_cast(substeps); + float linearDamping = hitbox.linearDamping * hitbox.friction; + + glm::vec3& pos = hitbox.position; + glm::vec3& vel = hitbox.velocity; + + bool prevGrounded = hitbox.grounded; + hitbox.grounded = false; + for (uint i = 0; i < substeps; i++) { + calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt); + } + + if (prevGrounded && !hitbox.grounded && vel.y <= 0.0f) { + vel += hitbox.groundVelocity; + hitbox.groundVelocity = {}; + } + + vel.x /= 1.0f + delta * linearDamping; + vel.z /= 1.0f + delta * linearDamping; + if (hitbox.verticalDamping > 0.0f) { + vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping; + } + + AABB aabb; + aabb.a = pos - hitbox.getHalfSize(); + aabb.b = pos + hitbox.getHalfSize(); + for (size_t i = 0; i < sensors.size(); i++) { + auto& sensor = *sensors[i]; + if (sensor.entity == entity) { + continue; + } + + bool triggered = false; + switch (sensor.type) { + case SensorType::AABB: + triggered = aabb.intersects(sensor.calculated.aabb); + break; + case SensorType::RADIUS: + triggered = glm::distance2( + pos, glm::vec3(sensor.calculated.radial)) + < sensor.calculated.radial.w; + break; + } + if (triggered) { + if (sensor.prevEntered.find(entity) == sensor.prevEntered.end()) { + sensor.enterCallback(sensor.entity, sensor.index, entity); + } + sensor.nextEntered.insert(entity); + } + } +} + void PhysicsSolver::removeSensor(Sensor* sensor) { sensors.erase(std::remove(sensors.begin(), sensors.end(), sensor), sensors.end()); } From ad5089f38300a6249a2a8fa707640b201bb3a5e1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Feb 2026 03:34:17 +0300 Subject: [PATCH 09/73] fix crouching check --- src/maths/aabb.hpp | 9 ++-- src/physics/PhysicsSolver.cpp | 80 +++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index d5dc9493b..ca19a81fe 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -47,14 +47,14 @@ struct AABB { } /// @brief Multiply AABB size from center - inline void scale(const glm::vec3 mul) { + inline void scale(const glm::vec3& mul) { glm::vec3 center = (a + b) * 0.5f; a = (a - center) * mul + center; b = (b - center) * mul + center; } /// @brief Multiply AABB size from given origin - inline void scale(const glm::vec3 mul, const glm::vec3 orig) { + inline void scale(const glm::vec3& mul, const glm::vec3& orig) { glm::vec3 beg = min(); glm::vec3 end = max(); glm::vec3 center = glm::mix(beg, end, orig); @@ -101,8 +101,9 @@ struct AABB { inline bool intersects(const AABB& aabb) const { return ( - a.x <= aabb.b.x && b.x >= aabb.a.x && a.y <= aabb.b.y && - b.y >= aabb.a.y && a.z <= aabb.b.z && b.z >= aabb.a.z + a.x <= aabb.b.x && b.x >= aabb.a.x && + a.y <= aabb.b.y && b.y >= aabb.a.y && + a.z <= aabb.b.z && b.z >= aabb.a.z ); } diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index b36f4a337..ddacdb192 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -28,11 +28,11 @@ static float calc_step_height( aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); aabb = aabb + pos + glm::vec3(0.0f, stepHeight, 0.0f); if (stepHeight > 0.0f) { - for (int ix = 0; ix <= glm::ceil((half.x-E) * 2); ix++) { - float x = (pos.x-half.x) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z) + iz; - if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z, aabb)) { + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { + float x = (pos.x - half.x) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { + float z = (pos.z - half.z) + iz; + if (chunks.isObstacleAt(x, pos.y + half.y + stepHeight, z, aabb)) { return 0.0f; } } @@ -44,22 +44,23 @@ static float calc_step_height( template static void calc_collision_pos( const GlobalChunks& chunks, + const std::vector& solidHitboxes, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, float stepHeight, - bool collided[3] + bool (&collided)[3] ) { + glm::vec3 offset(0.0f, stepHeight + E, 0.0f); if (vel[nx] <= 0.0f) { return; } - glm::vec3 offset(0.0f, stepHeight + E * 2, 0.0f); for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { glm::vec3 coord; coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { coord[nz] = (pos[nz] - half[nz] + E) + iz; - coord[nx] = (pos[nx] + half[nx] + E) + E; + coord[nx] = pos[nx] + half[nx] + E * 2; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -84,22 +85,23 @@ static void calc_collision_pos( template static bool calc_collision_neg( const GlobalChunks& chunks, + const std::vector& solidHitboxes, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, float stepHeight, - bool collided[3] + bool (&collided)[3] ) { + glm::vec3 offset(0.0f, stepHeight + E, 0.0f); if (vel[nx] >= 0.0f) { return false; } - glm::vec3 offset(0.0f, stepHeight + E, 0.0f); for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { glm::vec3 coord; coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { coord[nz] = (pos[nz] - half[nz] + E) + iz; - coord[nx] = (pos[nx] - half[nx] - E) - E; + coord[nx] = pos[nx] - half[nx] - E * 2; auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); @@ -130,21 +132,21 @@ static bool calc_collision_neg_y( const glm::vec3& half, glm::vec3& groundVelocty ) { - for (const auto& box : solidHitboxes) { + for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { continue; } auto boxhalf = box->getHalfSize(); if (box->getAABB().intersects(AABB(pos - half, pos + half))) { - if (vel.y < 0.0f) { - vel.y = 0.0f; - } - float newx = box->position.y + boxhalf.y + half.y; - if (newx - pos.y <= 0.5f) { - pos.y = newx; + float newy = box->position.y + boxhalf.y + half.y; + if (pos.y < newy) { + pos.y = newy; } groundVelocty = box->velocity; - return true; + if (vel.y < 0.0f) { + vel.y = 0.0f; + return true; + } } } if (vel.y >= 0.0f) { @@ -155,9 +157,9 @@ static bool calc_collision_neg_y( glm::vec3 coord; coord.x = (pos.x-half.x+E) + ix; for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ - coord.z = (pos.z-half.z+E) + iz; - coord.y = (pos.y-half.y-E) - E; - + coord.z = (pos.z - half.z + E) + iz; + coord.y = pos.y - half.y - E * 2; + auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale.x = 1.0f - E * 4.0f; @@ -192,11 +194,11 @@ void PhysicsSolver::calcCollisions( auto prevPos = pos; - calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight, hitbox.collided); - calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_neg<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_pos<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); - calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight, hitbox.collided); - calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_neg<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_pos<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { hitbox.grounded = true; @@ -233,12 +235,10 @@ void PhysicsSolver::calcCollisions( // step on if (stepHeight > 0.0 && vel.y <= 0.0f) { - AABB boxAABB = AABB(-half, +half); + AABB boxAABB = AABB(pos - half, pos + half); boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f - E * 2, 1.0f - E * 2)); - boxAABB = boxAABB.translated(pos); - for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { - float x = (pos.x-half.x) + ix; + float x = (pos.x - half.x) + ix; for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { float z = (pos.z - half.z) + iz; float y = (pos.y - half.y + E) + E; @@ -252,7 +252,7 @@ void PhysicsSolver::calcCollisions( } } } - for (const auto& box : solidHitboxes) { + for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { continue; } @@ -306,19 +306,23 @@ void PhysicsSolver::calcSubstep( for (int axis = 0; axis <= 2; axis += 2) { hitbox.grounded = false; - AABB aabb(pos - half, pos + half); - aabb.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); + auto checkPos = pos; + checkPos.x = axis != 0 ? initpos.x : pos.x; + checkPos.z = axis == 0 ? initpos.z : pos.z; + + AABB boxAABB(checkPos - half, checkPos + half); + boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { - float x = (initpos.x - half.x + E) + ix; + float x = (pos.x - half.x + E) + ix; for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){ float z = (pos.z - half.z + E) + iz; - if (chunks.isObstacleAt(x, y, z, aabb)){ + if (chunks.isObstacleAt(x, y, z, boxAABB)){ hitbox.grounded = true; break; } } } - for (const auto& box : solidHitboxes) { + for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { continue; } @@ -395,5 +399,7 @@ void PhysicsSolver::step( } void PhysicsSolver::removeSensor(Sensor* sensor) { - sensors.erase(std::remove(sensors.begin(), sensors.end(), sensor), sensors.end()); + sensors.erase( + std::remove(sensors.begin(), sensors.end(), sensor), sensors.end() + ); } From 6aa7739c426310cc73fc6572d57a4477ea71d4fe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Feb 2026 18:45:31 +0300 Subject: [PATCH 10/73] feat: horizontal solid entities collision & increase code duplication --- src/physics/PhysicsSolver.cpp | 70 ++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index ddacdb192..68765b1ef 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -52,6 +52,37 @@ static void calc_collision_pos( bool (&collided)[3] ) { glm::vec3 offset(0.0f, stepHeight + E, 0.0f); + for (auto box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale[nz] = 1.0f - E * 2.0f; + scale[ny] = 1.0f - E * 2.0f; + boxAABB.scale(scale); + boxAABB = boxAABB + offset; + boxAABB.b.y -= stepHeight + E * 2; + + if (box->getAABB().intersects(boxAABB)) { + float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; + float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; + float newx; + if (glm::abs(newnegx - pos[nx]) < glm::abs(newposx - pos[nx])) { + newx = newnegx; + } else { + continue; + } + if (pos[nx] > newx) { + if (vel[nx] > 0.0f) { + vel[nx] = 0.0f; + } + pos[nx] = newx; + collided[nx] = true; + } + } + } if (vel[nx] <= 0.0f) { return; } @@ -93,6 +124,37 @@ static bool calc_collision_neg( bool (&collided)[3] ) { glm::vec3 offset(0.0f, stepHeight + E, 0.0f); + for (auto box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale[nz] = 1.0f - E * 2.0f; + scale[ny] = 1.0f - E * 2.0f; + boxAABB.scale(scale); + boxAABB = boxAABB + offset; + boxAABB.b.y -= stepHeight + E * 2; + + if (box->getAABB().intersects(boxAABB)) { + float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; + float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; + float newx; + if (glm::abs(newnegx - pos[nx]) > glm::abs(newposx - pos[nx])) { + newx = newposx; + } else { + continue; + } + if (pos[nx] < newx) { + if (vel[nx] < 0.0f) { + vel[nx] = 0.0f; + } + pos[nx] = newx; + collided[nx] = true; + } + } + } if (vel[nx] >= 0.0f) { return false; } @@ -136,8 +198,14 @@ static bool calc_collision_neg_y( if (glm::distance2(box->position, pos) < E) { continue; } + auto boxAABB = AABB(pos - half, pos + half); + glm::vec3 scale(1.0f); + scale.x = 1.0f - E * 4.0f; + scale.z = 1.0f - E * 4.0f; + boxAABB.scale(scale); + auto boxhalf = box->getHalfSize(); - if (box->getAABB().intersects(AABB(pos - half, pos + half))) { + if (box->getAABB().intersects(boxAABB)) { float newy = box->position.y + boxhalf.y + half.y; if (pos.y < newy) { pos.y = newy; From 8dd9e42764ff988c495e804119c6f9eff3ec30cc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Feb 2026 23:35:42 +0300 Subject: [PATCH 11/73] fix crouching check --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 68765b1ef..58c48ce1e 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -394,7 +394,7 @@ void PhysicsSolver::calcSubstep( if (glm::distance2(box->position, pos) < E) { continue; } - if (box->getAABB().intersects(AABB(pos - half, pos + half))) { + if (box->getAABB().intersects(boxAABB)) { hitbox.grounded = true; break; } From e98996e6a97f86a82ebb71cbd1da838b144639d4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Feb 2026 23:55:00 +0300 Subject: [PATCH 12/73] feat: ceiling collision for solid entities --- src/physics/PhysicsSolver.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 58c48ce1e..400952567 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -274,7 +274,7 @@ void PhysicsSolver::calcCollisions( if (vel.y > 0.0f){ AABB boxAABB = AABB(-half, +half); - boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f + E * 2, 1.0f - E * 2)); + boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f + E * 2, 1.0f - E * 4)); boxAABB = boxAABB.translated(pos); for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { @@ -293,6 +293,22 @@ void PhysicsSolver::calcCollisions( } } } + for (auto box : solidHitboxes) { + if (glm::distance2(box->position, pos) < E) { + continue; + } + auto boxhalf = box->getHalfSize(); + if (box->getAABB().intersects(boxAABB)) { + float newy = box->position.y - boxhalf.y - half.y; + if (pos.y > newy) { + pos.y = newy; + } + if (vel.y > 0.0f) { + vel.y = 0.0f; + break; + } + } + } } for (int axis = 0; axis < 3; axis++) { From aed4f788b5c1894dbfeec0188745f961719a5908 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Mar 2026 00:50:36 +0300 Subject: [PATCH 13/73] fixes --- src/physics/PhysicsSolver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 400952567..343506e97 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -207,7 +207,7 @@ static bool calc_collision_neg_y( auto boxhalf = box->getHalfSize(); if (box->getAABB().intersects(boxAABB)) { float newy = box->position.y + boxhalf.y + half.y; - if (pos.y < newy) { + if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } groundVelocty = box->velocity; @@ -236,7 +236,7 @@ static bool calc_collision_neg_y( if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { float newy = std::floor(coord.y) + half.y + aabb->max().y; - if (newy > pos.y) { + if (newy >= pos.y) { vel.y = 0.0f; pos.y = newy; } @@ -284,7 +284,7 @@ void PhysicsSolver::calcCollisions( float y = (pos.y+half.y+E) + 0.5f; if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ float newy = std::floor(y) - half.y + aabb->min().y - E; - if (pos.y > newy) { + if (pos.y >= newy) { vel.y = 0.0f; pos.y = newy; hitbox.collided[1] = true; @@ -300,7 +300,7 @@ void PhysicsSolver::calcCollisions( auto boxhalf = box->getHalfSize(); if (box->getAABB().intersects(boxAABB)) { float newy = box->position.y - boxhalf.y - half.y; - if (pos.y > newy) { + if (pos.y > newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } if (vel.y > 0.0f) { From 72d9f126134aed565e9418521c40053d94fa90f8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Mar 2026 21:50:46 +0300 Subject: [PATCH 14/73] calibration --- src/physics/PhysicsSolver.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 343506e97..a5f69ea85 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -59,13 +59,13 @@ static void calc_collision_pos( auto boxhalf = box->getHalfSize(); auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 2.0f; + scale[nz] = 1.0f - E * 8.0f; scale[ny] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; boxAABB.b.y -= stepHeight + E * 2; - if (box->getAABB().intersects(boxAABB)) { + if (box->position[nx] > pos[nx] && box->getAABB().intersects(boxAABB)) { float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; float newx; @@ -95,7 +95,7 @@ static void calc_collision_pos( auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 2.0f; + scale[nz] = 1.0f - E * 8.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; boxAABB.b.y -= stepHeight + E * 2; @@ -114,7 +114,7 @@ static void calc_collision_pos( } template -static bool calc_collision_neg( +static void calc_collision_neg( const GlobalChunks& chunks, const std::vector& solidHitboxes, glm::vec3& pos, @@ -131,13 +131,13 @@ static bool calc_collision_neg( auto boxhalf = box->getHalfSize(); auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 2.0f; + scale[nz] = 1.0f - E * 8.0f; scale[ny] = 1.0f - E * 2.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; boxAABB.b.y -= stepHeight + E * 2; - if (box->getAABB().intersects(boxAABB)) { + if (box->position[nx] < pos[nx] && box->getAABB().intersects(boxAABB)) { float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; float newx; @@ -156,7 +156,7 @@ static bool calc_collision_neg( } } if (vel[nx] >= 0.0f) { - return false; + return; } for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { glm::vec3 coord; @@ -167,7 +167,7 @@ static bool calc_collision_neg( auto boxAABB = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 2.0f; + scale[nz] = 1.0f - E * 8.0f; boxAABB.scale(scale); boxAABB = boxAABB + offset; boxAABB.b.y -= stepHeight + E * 2; @@ -179,11 +179,11 @@ static bool calc_collision_neg( pos[nx] = newx; collided[nx] = true; } - return true; + return; } } } - return false; + return; } static bool calc_collision_neg_y( @@ -205,7 +205,7 @@ static bool calc_collision_neg_y( boxAABB.scale(scale); auto boxhalf = box->getHalfSize(); - if (box->getAABB().intersects(boxAABB)) { + if (box->position.y < pos.y && box->getAABB().intersects(boxAABB)) { float newy = box->position.y + boxhalf.y + half.y; if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; @@ -265,8 +265,12 @@ void PhysicsSolver::calcCollisions( calc_collision_neg<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); calc_collision_pos<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + float xpos = pos.x; + pos.x = prevPos.x; + calc_collision_neg<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); calc_collision_pos<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + pos.x = xpos; if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { hitbox.grounded = true; @@ -298,7 +302,7 @@ void PhysicsSolver::calcCollisions( continue; } auto boxhalf = box->getHalfSize(); - if (box->getAABB().intersects(boxAABB)) { + if (box->position.y > pos.y && box->getAABB().intersects(boxAABB)) { float newy = box->position.y - boxhalf.y - half.y; if (pos.y > newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; @@ -327,7 +331,9 @@ void PhysicsSolver::calcCollisions( float z = (pos.z - half.z) + iz; float y = (pos.y - half.y + E) + E; if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) { - vel.y = 0.0f; + if (vel.y < 0.0f) { + vel.y = 0.0f; + } float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy - pos.y) <= stepHeight) { pos.y = newy; @@ -344,7 +350,7 @@ void PhysicsSolver::calcCollisions( if (box->getAABB().intersects(boxAABB)) { vel.y = 0.0f; float newy = box->position.y + boxhalf.y + half.y; - if (std::abs(newy - pos.y) <= stepHeight) { + if (std::abs(newy - pos.y) <= stepHeight + E * 4) { pos.y = newy; } } From 7f09c47ffbca1f5d6f3dfc9e6d9f9df416ede2ee Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Mar 2026 19:58:23 +0300 Subject: [PATCH 15/73] improve it cautiously --- src/physics/PhysicsSolver.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index a5f69ea85..225a9ecbb 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -66,8 +66,8 @@ static void calc_collision_pos( boxAABB.b.y -= stepHeight + E * 2; if (box->position[nx] > pos[nx] && box->getAABB().intersects(boxAABB)) { - float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; - float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; + float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; + float newposx = box->position[nx] + boxhalf[nx] + half[nx]; float newx; if (glm::abs(newnegx - pos[nx]) < glm::abs(newposx - pos[nx])) { newx = newnegx; @@ -79,7 +79,6 @@ static void calc_collision_pos( vel[nx] = 0.0f; } pos[nx] = newx; - collided[nx] = true; } } } @@ -138,8 +137,8 @@ static void calc_collision_neg( boxAABB.b.y -= stepHeight + E * 2; if (box->position[nx] < pos[nx] && box->getAABB().intersects(boxAABB)) { - float newnegx = box->position[nx] - boxhalf[nx] - half[nx] - E; - float newposx = box->position[nx] + boxhalf[nx] + half[nx] + E; + float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; + float newposx = box->position[nx] + boxhalf[nx] + half[nx]; float newx; if (glm::abs(newnegx - pos[nx]) > glm::abs(newposx - pos[nx])) { newx = newposx; @@ -151,7 +150,6 @@ static void calc_collision_neg( vel[nx] = 0.0f; } pos[nx] = newx; - collided[nx] = true; } } } @@ -448,7 +446,7 @@ void PhysicsSolver::step( calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt); } - if (prevGrounded && !hitbox.grounded && vel.y <= 0.0f) { + if (prevGrounded && !hitbox.grounded) { vel += hitbox.groundVelocity; hitbox.groundVelocity = {}; } From 2c5074172bf94519ed3e0e5de8835b26b3038d40 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Mar 2026 20:01:47 +0300 Subject: [PATCH 16/73] fix libpack.md formatting --- doc/en/scripting/builtins/libpack.md | 17 +++++++++-------- doc/ru/scripting/builtins/libpack.md | 3 ++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/doc/en/scripting/builtins/libpack.md b/doc/en/scripting/builtins/libpack.md index 2daab1269..651a030d2 100644 --- a/doc/en/scripting/builtins/libpack.md +++ b/doc/en/scripting/builtins/libpack.md @@ -78,14 +78,14 @@ Returns the id of all base packages (non-removeable) ```lua pack.get_info(packid: str) -> { - id: str, - title: str, - creator: str, - description: str, - version: str, - path: str, - icon: str, -- not available in headless mode - dependencies: optional strings array + id: str, + title: str, + creator: str, + description: str, + version: str, + path: str, + icon: str, -- not available in headless mode + dependencies: optional strings array } ``` @@ -95,6 +95,7 @@ Returns information about the pack (not necessarily installed). - `!` - required - `?` - optional - `~` - weak + for example `!teal` To obtain information about multiple packs, use table of ids to avoid re-scanning:one diff --git a/doc/ru/scripting/builtins/libpack.md b/doc/ru/scripting/builtins/libpack.md index 0c4aac0c1..925cad7f8 100644 --- a/doc/ru/scripting/builtins/libpack.md +++ b/doc/ru/scripting/builtins/libpack.md @@ -82,6 +82,7 @@ pack.get_info(packid: string) -> { - `!` - required - `?` - optional - `~` - weak + например `!teal` Для получения информации о нескольких паках используйте таблицу id, чтобы не @@ -101,4 +102,4 @@ pack.assemble(packids: table) -> table pack.request_writeable(packid: string, callback: function(string)) ``` -Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback. \ No newline at end of file +Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback. From 38354c42f9acfb956ff99f480ab0a1ecc84744b9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Mar 2026 22:18:20 +0300 Subject: [PATCH 17/73] applying velocity of ground the worst way possible --- src/logic/PlayerController.cpp | 4 +- .../scripting/lua/libs/lib__transform.cpp | 2 +- src/objects/Entities.cpp | 52 +++++++++++-------- src/objects/Player.cpp | 2 +- src/physics/Hitbox.cpp | 3 +- src/physics/Hitbox.hpp | 17 ++++++ src/physics/PhysicsSolver.cpp | 26 +++++++--- src/physics/PhysicsSolver.hpp | 3 +- 8 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index e707d1637..35ea961ec 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -229,8 +229,8 @@ PlayerController::PlayerController( } void PlayerController::onFootstep(const Hitbox& hitbox) { - auto pos = hitbox.position; - auto half = hitbox.halfsize; + const auto& pos = hitbox.position; + const auto& half = hitbox.getHalfSize(); for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { for (int offsetX = -1; offsetX <= 1; offsetX++) { diff --git a/src/logic/scripting/lua/libs/lib__transform.cpp b/src/logic/scripting/lua/libs/lib__transform.cpp index 7cf7bed62..16662daaa 100644 --- a/src/logic/scripting/lua/libs/lib__transform.cpp +++ b/src/logic/scripting/lua/libs/lib__transform.cpp @@ -31,7 +31,7 @@ static int l_set_pos(lua::State* L) { auto vec = lua::tovec3(L, 2); check_valid(vec); entity->getTransform().setPos(vec); - entity->getRigidbody().hitbox.position = vec; + entity->getRigidbody().hitbox.setPos(vec); } return 0; } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 53b606da8..de1e9796d 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -316,30 +316,36 @@ void Entities::updatePhysics(float delta) { auto view = registry->view(); auto physics = level.physics.get(); - for (auto [entity, eid, transform, rigidbody] : view.each()) { - if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) { - continue; - } - auto& hitbox = rigidbody.hitbox; - auto prevVel = hitbox.velocity; - bool grounded = hitbox.grounded; - float vel = glm::length(prevVel); - int substeps = static_cast(delta * vel * 20); - substeps = std::min(100, std::max(2, substeps)); - physics->step(*level.chunks, hitbox, delta, substeps, eid.uid); - hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f) - ? 8.0f - : (!grounded ? 2.0f : 10.0f); - hitbox.scale = transform.size; - transform.setPos(hitbox.position); - if (hitbox.grounded && !grounded) { - scripting::on_entity_grounded( - *get(eid.uid), glm::length(prevVel - hitbox.velocity) - ); - } - if (!hitbox.grounded && grounded) { - scripting::on_entity_fall(*get(eid.uid)); + for (int solid = false; solid <= true; solid++) { + for (auto [entity, eid, transform, rigidbody] : view.each()) { + if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) { + continue; + } + if (eid.def.solid == solid) { + continue; + } + auto& hitbox = rigidbody.hitbox; + auto prevVel = hitbox.velocity; + bool grounded = hitbox.grounded; + + float vel = glm::length(prevVel); + int substeps = static_cast(delta * vel * 20); + substeps = std::min(100, std::max(2, substeps)); + physics->step(*level.chunks, hitbox, delta, substeps, eid.uid); + hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f) + ? 8.0f + : (!grounded ? 2.0f : 10.0f); + hitbox.scale = transform.size; + transform.setPos(hitbox.position); + if (hitbox.grounded && !grounded) { + scripting::on_entity_grounded( + *get(eid.uid), glm::length(prevVel - hitbox.velocity) + ); + } + if (!hitbox.grounded && grounded) { + scripting::on_entity_fall(*get(eid.uid)); + } } } } diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index a63341d49..b77a07fdd 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -114,7 +114,7 @@ void Player::teleport(glm::vec3 position) { this->position = position; if (auto entity = level.entities->get(eid)) { - entity->getRigidbody().hitbox.position = position; + entity->getRigidbody().hitbox.setPos(position); entity->getTransform().setPos(position); entity->setInterpolatedPosition(position); } diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp index adf078418..a72bdd939 100644 --- a/src/physics/Hitbox.cpp +++ b/src/physics/Hitbox.cpp @@ -6,5 +6,6 @@ Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize) : type(type), position(position), halfsize(halfsize), - velocity(0.0f,0.0f,0.0f) + velocity(0.0f,0.0f,0.0f), + prevPosition(position) {} diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 870e24725..799367b50 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -1,5 +1,7 @@ #pragma once +#define GLM_ENABLE_EXPERIMENTAL + #include "maths/aabb.hpp" #include "typedefs.hpp" #include "util/EnumMetadata.hpp" @@ -8,6 +10,7 @@ #include #include #include +#include enum class SensorType { AABB, @@ -62,6 +65,13 @@ struct Hitbox { bool crouching = false; float stepHeight = 0.5f; glm::vec3 groundVelocity {}; + + // garbage tbh + glm::vec3 prevPosition {}; + int substeps = 1; + float delta = 0.0f; + + static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f; Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize); @@ -72,4 +82,11 @@ struct Hitbox { glm::vec3 getHalfSize() const { return halfsize * scale; } + + void setPos(const glm::vec3& vec) { + position = vec; + if (glm::distance2(position, prevPosition) >= TELEPORT_THRESOLD_SQR) { + prevPosition = vec; + } + } }; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 225a9ecbb..ee47f0a8b 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -208,7 +208,7 @@ static bool calc_collision_neg_y( if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } - groundVelocty = box->velocity; + groundVelocty = (box->position - box->prevPosition) / box->delta; if (vel.y < 0.0f) { vel.y = 0.0f; return true; @@ -362,8 +362,14 @@ void PhysicsSolver::calcSubstep( glm::vec3& vel, glm::vec3& pos, bool prevGrounded, - float dt + float dt, + int substeps ) { + if (hitbox.grounded) { + pos.x += hitbox.groundVelocity.x * dt * substeps; + pos.z += hitbox.groundVelocity.z * dt * substeps; + } + auto initpos = pos; auto half = hitbox.getHalfSize(); float gravityScale = hitbox.gravityScale; @@ -434,6 +440,10 @@ void PhysicsSolver::step( uint substeps, entityid_t entity ) { + hitbox.prevPosition = hitbox.position; + hitbox.substeps = substeps; + hitbox.delta = delta; + float dt = delta / static_cast(substeps); float linearDamping = hitbox.linearDamping * hitbox.friction; @@ -443,12 +453,7 @@ void PhysicsSolver::step( bool prevGrounded = hitbox.grounded; hitbox.grounded = false; for (uint i = 0; i < substeps; i++) { - calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt); - } - - if (prevGrounded && !hitbox.grounded) { - vel += hitbox.groundVelocity; - hitbox.groundVelocity = {}; + calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt, substeps); } vel.x /= 1.0f + delta * linearDamping; @@ -456,6 +461,11 @@ void PhysicsSolver::step( if (hitbox.verticalDamping > 0.0f) { vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping; } + if (prevGrounded && !hitbox.grounded) { + auto appliedVelocity = hitbox.groundVelocity; + vel += appliedVelocity; + hitbox.groundVelocity = {}; + } AABB aabb; aabb.a = pos - hitbox.getHalfSize(); diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 052cf58e3..888c239d3 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -53,6 +53,7 @@ private: glm::vec3& vel, glm::vec3& pos, bool prevGrounded, - float dt + float dt, + int substeps ); }; From 262e72c36ea688ff55d89933996acbef54620c90 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Mar 2026 22:32:24 +0300 Subject: [PATCH 18/73] fix AssetsManagement destructor --- src/engine/AssetsManagement.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/AssetsManagement.cpp b/src/engine/AssetsManagement.cpp index 1ca1935c9..13df11220 100644 --- a/src/engine/AssetsManagement.cpp +++ b/src/engine/AssetsManagement.cpp @@ -18,6 +18,8 @@ AssetsManagement::AssetsManagement(Engine& engine) AssetsManagement::~AssetsManagement() { finishBackgroundLoader(); + assets.reset(); + assetsVault.clearKeepedObjects(); } const Assets* AssetsManagement::getStorage() const { From 7da5f8ed0ebd4bc533fd0164715946c50f17e33b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Mar 2026 00:54:59 +0300 Subject: [PATCH 19/73] update doc/*/xml-ui-layouts.md --- doc/en/xml-ui-layouts.md | 8 +++++--- doc/ru/xml-ui-layouts.md | 9 ++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index 97b36d1f6..aeb71dd62 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -213,9 +213,6 @@ Element must be in direct sub-element of *inventory*. ## *slots-grid* - `start-index` - inventory slot index of the first slot. Type: integer -- `rows` - number of grid rows (unnecessary if *cols* and *count* specified). Type: integer -- `cols` - number of grid columns (unnecessary if *rows* and *count* specified). Type: integer -- `count` - total number of slots in grid (unnecessary if *rows* and *cols* specified). Type: integer - `interval` - visual slots interval. Type: number - `padding` - grid padding (not slots interval). Type: number. (*deprecated*) - `sharefunc` - Lua event called on LMB + Shift. Inventory id and slot index passed as arguments. @@ -223,3 +220,8 @@ Element must be in direct sub-element of *inventory*. - `onrightclick` - Lua event called on RMB click. Inventory id and slot index passed as arguments. - `taking` - the ability to take an item from a slot. - `placing` - the ability to put an item in a slot. + +Slots configuration (just specify two attributes): +- `rows` - ​​number of rows. Type: integer +- `cols` - number of columns. Type: integer +- `count` - total number of slots. Type: integer diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index aeb669b3f..2e93e6f0a 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -214,10 +214,8 @@ ## Сетка слотов - *slots-grid* Элемент должен находиться внутри `inventory` элемента, без посредников. + - `start-index` - индекс первого слота -- `rows` - число рядов (не указывается, если указано cols). -- `cols` - число столбцов (не указывается, если указано rows). -- `count` - общее число слотов (не указывается, если указаны rows и cols). - `interval` - интервал между слотами. Тип: число. - `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*) - `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота @@ -225,3 +223,8 @@ - `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота - `taking` - возможность взять предмет из слота. - `placing` - возможность положить предмет в слот. + +Конфигурация слотов (достаточно указать два атрибута): +- `rows` - число рядов. Тип: число. +- `cols` - число столбцов. Тип: число. +- `count` - общее число слотов. Тип: число. From 4f42d30f91900bd408e52781a0336cd36e334648 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 15:49:25 +1000 Subject: [PATCH 20/73] fixed missing includes in EngineController.hpp --- src/logic/EngineController.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/logic/EngineController.hpp b/src/logic/EngineController.hpp index a46973cf3..baba7ba9e 100644 --- a/src/logic/EngineController.hpp +++ b/src/logic/EngineController.hpp @@ -3,6 +3,7 @@ #include #include #include +#include class Engine; class World; From 6644c505f00b5650fb623022aba0d40029bab937 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 15:58:45 +1000 Subject: [PATCH 21/73] added quat.from_euler lua function --- src/logic/scripting/lua/libs/libquat.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/logic/scripting/lua/libs/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp index 2316360a1..286c6e05a 100644 --- a/src/logic/scripting/lua/libs/libquat.cpp +++ b/src/logic/scripting/lua/libs/libquat.cpp @@ -44,8 +44,18 @@ static int l_tostring(lua::State* L) { return lua::pushstring(L, ss.str()); } +static int l_from_euler(lua::State *L) +{ + uint argc = lua::check_argc(L, 1, 2); + glm::vec3 euler = lua::tovec3(L, 1); + + if (argc == 1) return lua::pushquat(L, glm::quat(euler)); + return lua::setquat(L, 2, glm::quat(euler)); +} + const luaL_Reg quatlib[] = { {"from_mat4", lua::wrap}, + {"from_euler", lua::wrap}, {"slerp", lua::wrap}, {"tostring", lua::wrap}, {nullptr, nullptr} From a0963c5ee68e4245b7c7b65e3e3b244ffa0f2502 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 16:30:12 +1000 Subject: [PATCH 22/73] added quat.from_euler function to docs & fixed mistake in same page --- doc/ru/scripting/builtins/libquat.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md index 5a6495658..07b0b991c 100644 --- a/doc/ru/scripting/builtins/libquat.md +++ b/doc/ru/scripting/builtins/libquat.md @@ -2,7 +2,7 @@ Библиотека для работы с кватернионами. -## Кватернион из матрицы - *mat4.from_quat(...)* +## Кватернион из матрицы - *quat.from_mat4(...)* ```lua -- создает кватернион на основе матрицы вращения @@ -12,6 +12,16 @@ quat.from_mat4(m: matrix) -> quat quat.from_mat4(m: matrix, dst: quat) ``` +## Кватернион из углов Эйлера - *quat.from_euler(...)* + +```lua +-- создает кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) +quat.from_euler(euler: vec3) -> quat + +-- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) +quat.from_euler(euler: vec3, dst: quat) +``` + ## Сферическая линейная интерполяция - *quat.slerp(...)* Интерполяция всегда выполняется по короткому пути, а вращение выполняется с постоянной скоростью. From 3f5b194fcbcf19e62aa4811c2e4178d54061e01b Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 16:34:47 +1000 Subject: [PATCH 23/73] misc changed logic in quat.from_euler --- src/logic/scripting/lua/libs/libquat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logic/scripting/lua/libs/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp index 286c6e05a..d08983fe4 100644 --- a/src/logic/scripting/lua/libs/libquat.cpp +++ b/src/logic/scripting/lua/libs/libquat.cpp @@ -49,8 +49,8 @@ static int l_from_euler(lua::State *L) uint argc = lua::check_argc(L, 1, 2); glm::vec3 euler = lua::tovec3(L, 1); - if (argc == 1) return lua::pushquat(L, glm::quat(euler)); - return lua::setquat(L, 2, glm::quat(euler)); + if (argc == 2) return lua::setquat(L, 2, glm::quat(euler)); + return lua::pushquat(L, glm::quat(euler)); } const luaL_Reg quatlib[] = { From 6936db8b461757278913b7b11735a8e8d7ba367f Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 16:42:29 +1000 Subject: [PATCH 24/73] =?UTF-8?q?replaced=20'=D0=B5'=20on=20'=D1=91'=20in?= =?UTF-8?q?=20some=20words?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/ru/scripting/builtins/libquat.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md index 07b0b991c..81878fe90 100644 --- a/doc/ru/scripting/builtins/libquat.md +++ b/doc/ru/scripting/builtins/libquat.md @@ -5,7 +5,7 @@ ## Кватернион из матрицы - *quat.from_mat4(...)* ```lua --- создает кватернион на основе матрицы вращения +-- создаёт кватернион на основе матрицы вращения quat.from_mat4(m: matrix) -> quat -- записывает кватернион по матрице вращения в dst @@ -15,7 +15,7 @@ quat.from_mat4(m: matrix, dst: quat) ## Кватернион из углов Эйлера - *quat.from_euler(...)* ```lua --- создает кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) +-- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) quat.from_euler(euler: vec3) -> quat -- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) @@ -27,7 +27,7 @@ quat.from_euler(euler: vec3, dst: quat) Интерполяция всегда выполняется по короткому пути, а вращение выполняется с постоянной скоростью. ```lua --- создает кватернион как интерполяцию между a и b, +-- создаёт кватернион как интерполяцию между a и b, -- где t - фактор интерполяции quat.slerp(a: quat, b: quat, t: number) -> quat @@ -39,6 +39,6 @@ quat.slerp(a: quat, b: quat, t: number, dst: quat) ## Перевод в строку - *quat.tostring(...)* ```lua --- возвращает строку представляющую содержимое кватерниона +-- возвращает строку, представляющую содержимое кватерниона quat.tostring(q: quat) -> string ``` From 3bbc8427e74362a2514b1a0bbd80c9a7b62f6591 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 19:20:26 +1000 Subject: [PATCH 25/73] changed formatting style in libquat.cpp --- src/logic/scripting/lua/libs/libquat.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/logic/scripting/lua/libs/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp index d08983fe4..2f3fad9ff 100644 --- a/src/logic/scripting/lua/libs/libquat.cpp +++ b/src/logic/scripting/lua/libs/libquat.cpp @@ -44,8 +44,7 @@ static int l_tostring(lua::State* L) { return lua::pushstring(L, ss.str()); } -static int l_from_euler(lua::State *L) -{ +static int l_from_euler(lua::State *L) { uint argc = lua::check_argc(L, 1, 2); glm::vec3 euler = lua::tovec3(L, 1); From 415d5a87f7d0a8bbd7245b7cf3a8c2c84b68546c Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 19:37:31 +1000 Subject: [PATCH 26/73] fixed docs --- doc/ru/scripting/builtins/libquat.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md index 81878fe90..e5b69e35e 100644 --- a/doc/ru/scripting/builtins/libquat.md +++ b/doc/ru/scripting/builtins/libquat.md @@ -16,9 +16,11 @@ quat.from_mat4(m: matrix, dst: quat) ```lua -- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) +-- (значения углов В РАДИАНАХ) quat.from_euler(euler: vec3) -> quat -- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) +-- (значения углов В РАДИАНАХ) quat.from_euler(euler: vec3, dst: quat) ``` From 1de10e751de6c0d45151c89a6f02cf79cf14ae99 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 19:38:29 +1000 Subject: [PATCH 27/73] fixed docs x2 --- doc/ru/scripting/builtins/libquat.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md index e5b69e35e..0cc05c98e 100644 --- a/doc/ru/scripting/builtins/libquat.md +++ b/doc/ru/scripting/builtins/libquat.md @@ -16,11 +16,11 @@ quat.from_mat4(m: matrix, dst: quat) ```lua -- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) --- (значения углов В РАДИАНАХ) +-- (значения углов строго в радианах) quat.from_euler(euler: vec3) -> quat -- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) --- (значения углов В РАДИАНАХ) +-- (значения углов строго в радианах) quat.from_euler(euler: vec3, dst: quat) ``` From 563b210500d16e7996acf6e39f6c64ca392cbbfb Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Wed, 4 Mar 2026 19:45:21 +1000 Subject: [PATCH 28/73] replaced radians on degrees --- doc/ru/scripting/builtins/libquat.md | 4 ++-- src/logic/scripting/lua/libs/libquat.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md index 0cc05c98e..e991de252 100644 --- a/doc/ru/scripting/builtins/libquat.md +++ b/doc/ru/scripting/builtins/libquat.md @@ -16,11 +16,11 @@ quat.from_mat4(m: matrix, dst: quat) ```lua -- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) --- (значения углов строго в радианах) +-- (значения углов строго в градусах) quat.from_euler(euler: vec3) -> quat -- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll) --- (значения углов строго в радианах) +-- (значения углов строго в градусах) quat.from_euler(euler: vec3, dst: quat) ``` diff --git a/src/logic/scripting/lua/libs/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp index 2f3fad9ff..53757cb36 100644 --- a/src/logic/scripting/lua/libs/libquat.cpp +++ b/src/logic/scripting/lua/libs/libquat.cpp @@ -46,7 +46,7 @@ static int l_tostring(lua::State* L) { static int l_from_euler(lua::State *L) { uint argc = lua::check_argc(L, 1, 2); - glm::vec3 euler = lua::tovec3(L, 1); + glm::vec3 euler = glm::radians(lua::tovec3(L, 1)); if (argc == 2) return lua::setquat(L, 2, glm::quat(euler)); return lua::pushquat(L, glm::quat(euler)); From f726702f21e7d497cc906d05d23ceb9fb87d062e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Mar 2026 20:48:04 +0300 Subject: [PATCH 29/73] update substeps calculation scheme --- src/objects/Entities.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index de1e9796d..6750116df 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -281,7 +281,6 @@ void Entities::preparePhysics(float delta) { auto& physics = *level.physics; auto& solidHitboxes = physics.getSolidHitboxesWriteable(); - if (sensorsTickClock.update(delta)) { auto part = sensorsTickClock.getPart(); auto parts = sensorsTickClock.getParts(); @@ -330,7 +329,7 @@ void Entities::updatePhysics(float delta) { bool grounded = hitbox.grounded; float vel = glm::length(prevVel); - int substeps = static_cast(delta * vel * 20); + int substeps = static_cast(delta * (vel + 2.0f) * 20); substeps = std::min(100, std::max(2, substeps)); physics->step(*level.chunks, hitbox, delta, substeps, eid.uid); hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f) From 8e4e18a86a11517723f3571259c8cdba4548ae90 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Mar 2026 22:33:50 +0300 Subject: [PATCH 30/73] improve it less cautiously --- src/physics/Hitbox.hpp | 2 +- src/physics/PhysicsSolver.cpp | 50 ++++++++++++++++------------------- src/physics/PhysicsSolver.hpp | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 799367b50..0539f90f3 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -60,7 +60,7 @@ struct Hitbox { float friction = 1.0f; float verticalDamping = 1.0f; bool grounded = false; - bool collided[3] {}; + bool yCollided = false; float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index ee47f0a8b..9f302d4b7 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -43,14 +43,15 @@ static float calc_step_height( template static void calc_collision_pos( + Hitbox& hitbox, const GlobalChunks& chunks, const std::vector& solidHitboxes, - glm::vec3& pos, - glm::vec3& vel, const glm::vec3& half, - float stepHeight, - bool (&collided)[3] + float stepHeight ) { + auto& pos = hitbox.position; + auto& vel = hitbox.velocity; + glm::vec3 offset(0.0f, stepHeight + E, 0.0f); for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { @@ -82,7 +83,7 @@ static void calc_collision_pos( } } } - if (vel[nx] <= 0.0f) { + if (vel[nx] <= 0.0f && hitbox.groundVelocity[nx] <= 0.0f) { return; } for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { @@ -100,11 +101,10 @@ static void calc_collision_pos( boxAABB.b.y -= stepHeight + E * 2; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; + float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx]; if (pos[nx] > newx) { vel[nx] = 0.0f; pos[nx] = newx; - collided[nx] = true; } return; } @@ -114,14 +114,15 @@ static void calc_collision_pos( template static void calc_collision_neg( + Hitbox& hitbox, const GlobalChunks& chunks, const std::vector& solidHitboxes, - glm::vec3& pos, - glm::vec3& vel, const glm::vec3& half, - float stepHeight, - bool (&collided)[3] + float stepHeight ) { + auto& pos = hitbox.position; + auto& vel = hitbox.velocity; + glm::vec3 offset(0.0f, stepHeight + E, 0.0f); for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { @@ -153,7 +154,7 @@ static void calc_collision_neg( } } } - if (vel[nx] >= 0.0f) { + if (vel[nx] >= 0.0f && hitbox.groundVelocity[nx] >= 0.0f) { return; } for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { @@ -171,11 +172,10 @@ static void calc_collision_neg( boxAABB.b.y -= stepHeight + E * 2; if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E; + float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx]; if (pos[nx] < newx) { vel[nx] = 0.0f; pos[nx] = newx; - collided[nx] = true; } return; } @@ -250,24 +250,22 @@ void PhysicsSolver::calcCollisions( Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, - const glm::vec3 half, + const glm::vec3& half, float stepHeight ) { - hitbox.collided[0] = false; - hitbox.collided[1] = false; - hitbox.collided[2] = false; + hitbox.yCollided = false; stepHeight = calc_step_height(chunks, pos, half, stepHeight); auto prevPos = pos; - calc_collision_neg<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); - calc_collision_pos<0, 1, 2>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_neg<0, 1, 2>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision_pos<0, 1, 2>(hitbox, chunks, solidHitboxes, half, stepHeight); float xpos = pos.x; pos.x = prevPos.x; - calc_collision_neg<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); - calc_collision_pos<2, 1, 0>(chunks, solidHitboxes, pos, vel, half, stepHeight, hitbox.collided); + calc_collision_neg<2, 1, 0>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision_pos<2, 1, 0>(hitbox, chunks, solidHitboxes, half, stepHeight); pos.x = xpos; if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { @@ -289,7 +287,7 @@ void PhysicsSolver::calcCollisions( if (pos.y >= newy) { vel.y = 0.0f; pos.y = newy; - hitbox.collided[1] = true; + hitbox.yCollided = true; } break; } @@ -313,10 +311,8 @@ void PhysicsSolver::calcCollisions( } } - for (int axis = 0; axis < 3; axis++) { - if (hitbox.collided[axis]) { - pos[axis] = prevPos[axis]; - } + if (hitbox.yCollided) { + pos.y = prevPos.y; } // step on diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 888c239d3..419b37ecc 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -43,7 +43,7 @@ private: Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, - const glm::vec3 half, + const glm::vec3& half, float stepHeight ); From 546bbcb4e28b6ee3f25461d37d73fcb1dc772ce7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Mar 2026 23:29:56 +0300 Subject: [PATCH 31/73] reduce duplicating code by increasing complexity even more --- src/physics/PhysicsSolver.cpp | 135 ++++++++-------------------------- 1 file changed, 32 insertions(+), 103 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 9f302d4b7..618326dc1 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -41,8 +41,8 @@ static float calc_step_height( return stepHeight; } -template -static void calc_collision_pos( +template +static void calc_collision( Hitbox& hitbox, const GlobalChunks& chunks, const std::vector& solidHitboxes, @@ -58,32 +58,32 @@ static void calc_collision_pos( continue; } auto boxhalf = box->getHalfSize(); - auto boxAABB = AABB(pos - half, pos + half); + auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale[nz] = 1.0f - E * 8.0f; scale[ny] = 1.0f - E * 2.0f; - boxAABB.scale(scale); - boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight + E * 2; + aabb.scale(scale); + aabb = aabb + offset; + aabb.b.y -= stepHeight + E * 2; - if (box->position[nx] > pos[nx] && box->getAABB().intersects(boxAABB)) { + if ((box->position[nx] - pos[nx]) * sign > 0.0f && box->getAABB().intersects(aabb)) { float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; float newposx = box->position[nx] + boxhalf[nx] + half[nx]; float newx; - if (glm::abs(newnegx - pos[nx]) < glm::abs(newposx - pos[nx])) { - newx = newnegx; + if ((glm::abs(newnegx - pos[nx]) - glm::abs(newposx - pos[nx]))*sign < 0.0f) { + newx = sign > 0 ? newnegx : newposx; } else { continue; } - if (pos[nx] > newx) { - if (vel[nx] > 0.0f) { + if ((pos[nx] - newx) * sign > 0.0f) { + if (vel[nx] * sign > 0.0f) { vel[nx] = 0.0f; } pos[nx] = newx; } } } - if (vel[nx] <= 0.0f && hitbox.groundVelocity[nx] <= 0.0f) { + if (vel[nx] * sign <= 0.0f && hitbox.groundVelocity[nx] * sign <= 0.0f) { return; } for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { @@ -91,18 +91,19 @@ static void calc_collision_pos( coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { coord[nz] = (pos[nz] - half[nz] + E) + iz; - coord[nx] = pos[nx] + half[nx] + E * 2; + coord[nx] = pos[nx] + (half[nx] + E * 2) * sign; - auto boxAABB = AABB(pos - half, pos + half); + auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale[nz] = 1.0f - E * 8.0f; - boxAABB.scale(scale); - boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight + E * 2; + aabb.scale(scale); + aabb = aabb + offset; + aabb.b.y -= stepHeight + E * 2; - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx]; - if (pos[nx] > newx) { + if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { + float newx = std::floor(coord[nx]) - half[nx] * sign + + (sign > 0 ? obstacle->min() : obstacle->max())[nx]; + if ((pos[nx] - newx) * sign > 0.0f) { vel[nx] = 0.0f; pos[nx] = newx; } @@ -112,78 +113,6 @@ static void calc_collision_pos( } } -template -static void calc_collision_neg( - Hitbox& hitbox, - const GlobalChunks& chunks, - const std::vector& solidHitboxes, - const glm::vec3& half, - float stepHeight -) { - auto& pos = hitbox.position; - auto& vel = hitbox.velocity; - - glm::vec3 offset(0.0f, stepHeight + E, 0.0f); - for (auto box : solidHitboxes) { - if (glm::distance2(box->position, pos) < E) { - continue; - } - auto boxhalf = box->getHalfSize(); - auto boxAABB = AABB(pos - half, pos + half); - glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 8.0f; - scale[ny] = 1.0f - E * 2.0f; - boxAABB.scale(scale); - boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight + E * 2; - - if (box->position[nx] < pos[nx] && box->getAABB().intersects(boxAABB)) { - float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; - float newposx = box->position[nx] + boxhalf[nx] + half[nx]; - float newx; - if (glm::abs(newnegx - pos[nx]) > glm::abs(newposx - pos[nx])) { - newx = newposx; - } else { - continue; - } - if (pos[nx] < newx) { - if (vel[nx] < 0.0f) { - vel[nx] = 0.0f; - } - pos[nx] = newx; - } - } - } - if (vel[nx] >= 0.0f && hitbox.groundVelocity[nx] >= 0.0f) { - return; - } - for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) { - glm::vec3 coord; - coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy; - for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) { - coord[nz] = (pos[nz] - half[nz] + E) + iz; - coord[nx] = pos[nx] - half[nx] - E * 2; - - auto boxAABB = AABB(pos - half, pos + half); - glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 8.0f; - boxAABB.scale(scale); - boxAABB = boxAABB + offset; - boxAABB.b.y -= stepHeight + E * 2; - - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx]; - if (pos[nx] < newx) { - vel[nx] = 0.0f; - pos[nx] = newx; - } - return; - } - } - } - return; -} - static bool calc_collision_neg_y( const GlobalChunks& chunks, const std::vector& solidHitboxes, @@ -196,14 +125,14 @@ static bool calc_collision_neg_y( if (glm::distance2(box->position, pos) < E) { continue; } - auto boxAABB = AABB(pos - half, pos + half); + auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale.x = 1.0f - E * 4.0f; scale.z = 1.0f - E * 4.0f; - boxAABB.scale(scale); + aabb.scale(scale); auto boxhalf = box->getHalfSize(); - if (box->position.y < pos.y && box->getAABB().intersects(boxAABB)) { + if (box->position.y < pos.y && box->getAABB().intersects(aabb)) { float newy = box->position.y + boxhalf.y + half.y; if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; @@ -226,14 +155,14 @@ static bool calc_collision_neg_y( coord.z = (pos.z - half.z + E) + iz; coord.y = pos.y - half.y - E * 2; - auto boxAABB = AABB(pos - half, pos + half); + auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); scale.x = 1.0f - E * 4.0f; scale.z = 1.0f - E * 4.0f; - boxAABB.scale(scale); + aabb.scale(scale); - if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z, boxAABB)) { - float newy = std::floor(coord.y) + half.y + aabb->max().y; + if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { + float newy = std::floor(coord.y) + half.y + obstacle->max().y; if (newy >= pos.y) { vel.y = 0.0f; pos.y = newy; @@ -258,14 +187,14 @@ void PhysicsSolver::calcCollisions( auto prevPos = pos; - calc_collision_neg<0, 1, 2>(hitbox, chunks, solidHitboxes, half, stepHeight); - calc_collision_pos<0, 1, 2>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision<0, 1, 2, -1>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision<0, 1, 2, 1>(hitbox, chunks, solidHitboxes, half, stepHeight); float xpos = pos.x; pos.x = prevPos.x; - calc_collision_neg<2, 1, 0>(hitbox, chunks, solidHitboxes, half, stepHeight); - calc_collision_pos<2, 1, 0>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision<2, 1, 0, -1>(hitbox, chunks, solidHitboxes, half, stepHeight); + calc_collision<2, 1, 0, 1>(hitbox, chunks, solidHitboxes, half, stepHeight); pos.x = xpos; if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { From 8b48dcbf5acb70d92616566ed34087d639da2e1d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Mar 2026 23:34:04 +0300 Subject: [PATCH 32/73] cleanup a bit --- src/physics/PhysicsSolver.cpp | 51 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 618326dc1..3e260d4bd 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -66,21 +66,23 @@ static void calc_collision( aabb = aabb + offset; aabb.b.y -= stepHeight + E * 2; - if ((box->position[nx] - pos[nx]) * sign > 0.0f && box->getAABB().intersects(aabb)) { - float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; - float newposx = box->position[nx] + boxhalf[nx] + half[nx]; - float newx; - if ((glm::abs(newnegx - pos[nx]) - glm::abs(newposx - pos[nx]))*sign < 0.0f) { - newx = sign > 0 ? newnegx : newposx; - } else { - continue; - } - if ((pos[nx] - newx) * sign > 0.0f) { - if (vel[nx] * sign > 0.0f) { - vel[nx] = 0.0f; - } - pos[nx] = newx; + if ((box->position[nx] - pos[nx]) * sign <= 0.0f || !box->getAABB().intersects(aabb)) { + continue; + } + + float newnegx = box->position[nx] - boxhalf[nx] - half[nx]; + float newposx = box->position[nx] + boxhalf[nx] + half[nx]; + float newx; + if ((glm::abs(newnegx - pos[nx]) - glm::abs(newposx - pos[nx]))*sign < 0.0f) { + newx = sign > 0 ? newnegx : newposx; + } else { + continue; + } + if ((pos[nx] - newx) * sign > 0.0f) { + if (vel[nx] * sign > 0.0f) { + vel[nx] = 0.0f; } + pos[nx] = newx; } } if (vel[nx] * sign <= 0.0f && hitbox.groundVelocity[nx] * sign <= 0.0f) { @@ -148,10 +150,10 @@ static bool calc_collision_neg_y( return false; } groundVelocty = {}; - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { glm::vec3 coord; - coord.x = (pos.x-half.x+E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++){ + coord.x = (pos.x - half.x + E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { coord.z = (pos.z - half.z + E) + iz; coord.y = pos.y - half.y - E * 2; @@ -161,7 +163,8 @@ static bool calc_collision_neg_y( scale.z = 1.0f - E * 4.0f; aabb.scale(scale); - if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { + if (const auto obstacle = + chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { float newy = std::floor(coord.y) + half.y + obstacle->max().y; if (newy >= pos.y) { vel.y = 0.0f; @@ -206,12 +209,12 @@ void PhysicsSolver::calcCollisions( boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f + E * 2, 1.0f - E * 4)); boxAABB = boxAABB.translated(pos); - for (int ix = 0; ix <= glm::ceil((half.x-E)*2); ix++) { - float x = (pos.x-half.x+E) + ix; - for (int iz = 0; iz <= glm::ceil((half.z-E)*2); iz++) { - float z = (pos.z-half.z+E) + iz; - float y = (pos.y+half.y+E) + 0.5f; - if (auto aabb = chunks.isObstacleAt(x,y,z, boxAABB)){ + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { + float x = (pos.x - half.x + E) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { + float z = (pos.z - half.z + E) + iz; + float y = (pos.y + half.y + E) + 0.5f; + if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) { float newy = std::floor(y) - half.y + aabb->min().y - E; if (pos.y >= newy) { vel.y = 0.0f; From 3ab11f9ef80102abb31a2270d91da9b4c58d7460 Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Thu, 5 Mar 2026 15:18:40 +1000 Subject: [PATCH 33/73] added mat4.perspective function --- doc/ru/scripting/builtins/libmat4.md | 18 ++++++++++++++++-- src/logic/scripting/lua/libs/libmat4.cpp | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/ru/scripting/builtins/libmat4.md b/doc/ru/scripting/builtins/libmat4.md index 1531798a4..1b44df1ee 100644 --- a/doc/ru/scripting/builtins/libmat4.md +++ b/doc/ru/scripting/builtins/libmat4.md @@ -114,15 +114,29 @@ mat4.decompose(m: matrix) } или nil ``` -## Отслеживание точки *mat4.look_at(...)* +## Отслеживание точки - *mat4.look_at(...)* ```lua --- cоздает матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх. +-- cоздаёт матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх. mat4.look_at(eye: vec3, center: vec3, up: vec3) -- записывает матрицу вида в dst mat4.look_at(eye: vec3, center: vec3, up: vec3, dst: matrix) ``` +## Расчёт матрицы перспективы - *mat4.perspective* + +```lua +-- возвращает расчитанную матрицу перспективы. +-- параметр fov принимает значение вертикального поля зрения в градусах. +-- параметр ratio задаёт отношение сторон (сторона X к стороне Y, пример: для монитора 800:600 отношение сторон равно 4 к 3, или 4 / 3). +-- параметр near задаёт расстояние от камеры до плоскости Near. +-- параметр far задаёт расстояние от камеры до плоскости Far. +mat4.perspective(fov: number, ratio: number, near: number, far: number) -> mat4 + +-- расчитывает матрицу перспективы на основе переданных значений и записывает её в переменную dst. +mat4.perspective(fov: number, ratio: number, near: number, far: number, dst: mat4) -> nil +``` + ## Перевод в строку - *mat4.tostring(...)* ```lua diff --git a/src/logic/scripting/lua/libs/libmat4.cpp b/src/logic/scripting/lua/libs/libmat4.cpp index 081d99b5e..59f15e661 100644 --- a/src/logic/scripting/lua/libs/libmat4.cpp +++ b/src/logic/scripting/lua/libs/libmat4.cpp @@ -229,6 +229,20 @@ static int l_look_at(lua::State* L) { } } +static int l_perspective(lua::State *L) { + uint argc = lua::check_argc(L, 4, 5); + + double fov = glm::radians(lua::tonumber(L, 1)); + double ratio = lua::tonumber(L, 2); + double near = lua::tonumber(L, 3); + double far = lua::tonumber(L, 4); + + glm::mat4 mat = glm::perspective(fov, ratio, near, far); + + if (argc == 5) return lua::setmat4(L, 5, mat); + else return lua::pushmat4(L, mat); +} + static int l_from_quat(lua::State* L) { uint argc = lua::check_argc(L, 1, 2); auto quat = lua::toquat(L, 1); @@ -280,6 +294,7 @@ const luaL_Reg mat4lib[] = { {"determinant", lua::wrap}, {"decompose", lua::wrap}, {"look_at", lua::wrap}, + {"perspective", lua::wrap}, {"from_quat", lua::wrap}, {"tostring", lua::wrap}, {nullptr, nullptr} From 01d46b2690b700630e0f456459e53ab63f2a82fb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Mar 2026 20:34:35 +0300 Subject: [PATCH 34/73] add groundMaterial --- src/physics/Hitbox.hpp | 4 +++- src/physics/PhysicsSolver.cpp | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 0539f90f3..15b7acc89 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -64,11 +64,13 @@ struct Hitbox { float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; + std::string material; + glm::vec3 groundVelocity {}; + std::string groundMaterial; // garbage tbh glm::vec3 prevPosition {}; - int substeps = 1; float delta = 0.0f; static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 3e260d4bd..cfddcb783 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -116,13 +116,14 @@ static void calc_collision( } static bool calc_collision_neg_y( + Hitbox& hitbox, const GlobalChunks& chunks, const std::vector& solidHitboxes, - glm::vec3& pos, - glm::vec3& vel, - const glm::vec3& half, - glm::vec3& groundVelocty + const glm::vec3& half ) { + auto& pos = hitbox.position; + auto& vel = hitbox.velocity; + for (auto box : solidHitboxes) { if (glm::distance2(box->position, pos) < E) { continue; @@ -139,9 +140,13 @@ static bool calc_collision_neg_y( if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } - groundVelocty = (box->position - box->prevPosition) / box->delta; + hitbox.groundVelocity = + (box->position - box->prevPosition) / box->delta; if (vel.y < 0.0f) { vel.y = 0.0f; + if (hitbox.groundMaterial.empty()) { + hitbox.groundMaterial = hitbox.material; + } return true; } } @@ -149,7 +154,7 @@ static bool calc_collision_neg_y( if (vel.y >= 0.0f) { return false; } - groundVelocty = {}; + hitbox.groundVelocity = {}; for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { glm::vec3 coord; coord.x = (pos.x - half.x + E) + ix; @@ -200,7 +205,7 @@ void PhysicsSolver::calcCollisions( calc_collision<2, 1, 0, 1>(hitbox, chunks, solidHitboxes, half, stepHeight); pos.x = xpos; - if (calc_collision_neg_y(chunks, solidHitboxes, pos, vel, half, hitbox.groundVelocity)) { + if (calc_collision_neg_y(hitbox, chunks, solidHitboxes, half)) { hitbox.grounded = true; } @@ -369,8 +374,8 @@ void PhysicsSolver::step( entityid_t entity ) { hitbox.prevPosition = hitbox.position; - hitbox.substeps = substeps; hitbox.delta = delta; + hitbox.groundMaterial.clear(); float dt = delta / static_cast(substeps); float linearDamping = hitbox.linearDamping * hitbox.friction; From dbc7cd56bdad2b200008e2834b27f4702b0840b3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Mar 2026 20:52:45 +0300 Subject: [PATCH 35/73] feat: solid entity material support & minor refactor --- src/frontend/LevelFrontend.cpp | 102 ++++++++++++++++++--------- src/frontend/LevelFrontend.hpp | 14 ++-- src/frontend/hud.cpp | 4 +- src/frontend/screens/LevelScreen.cpp | 2 +- src/logic/PlayerController.cpp | 26 ++----- src/logic/PlayerController.hpp | 6 ++ 6 files changed, 91 insertions(+), 63 deletions(-) diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index 9a8553da6..d70f09ba2 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -10,17 +10,19 @@ #include "logic/LevelController.hpp" #include "logic/PlayerController.hpp" #include "objects/Player.hpp" +#include "physics/Hitbox.hpp" #include "voxels/Block.hpp" +#include "voxels/Chunks.hpp" #include "world/Level.hpp" #include "engine/Engine.hpp" LevelFrontend::LevelFrontend( Engine& engine, - Player* currentPlayer, - LevelController* controller, + PlayerController& playerController, + LevelController& controller, const EngineSettings& settings ) - : level(*controller->getLevel()), + : level(*controller.getLevel()), controller(controller), assets(*engine.getAssets()), contentCache(std::make_unique( @@ -36,45 +38,79 @@ LevelFrontend::LevelFrontend( "block-previews" ); - auto& rassets = assets; - controller->getBlocksController()->listenBlockInteraction( - [currentPlayer, controller, &rassets](auto player, const auto& pos, const auto& def, BlockInteraction type) { - const auto& level = *controller->getLevel(); + auto& currentPlayer = playerController.getPlayer(); + auto& assets = this->assets; + auto& level = this->level; + + playerController.setFootstepCallback( + [&level, ¤tPlayer, &assets](const Hitbox& hitbox) { + const BlockMaterial* material = nullptr; + if (hitbox.groundMaterial.empty()) { + const auto& pos = hitbox.position; + const auto& half = hitbox.getHalfSize(); + + auto& blockIndices = level.content.getIndices()->blocks; + for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { + for (int offsetX = -1; offsetX <= 1; offsetX++) { + int x = std::floor(pos.x + half.x * offsetX); + int y = std::floor(pos.y - half.y * 1.1f); + int z = std::floor(pos.z + half.z * offsetZ); + auto vox = currentPlayer.chunks->get(x, y, z); + if (vox) { + auto& def = blockIndices.require(vox->id); + if (!def.obstacle) { + continue; + } + material = level.content.findBlockMaterial(def.material); + break; + } + } + } + } else { + material = level.content.findBlockMaterial(hitbox.groundMaterial); + } + if (material == nullptr) { + return; + } + + auto sound = assets.get(material->stepsSound); + glm::vec3 pos {}; + auto soundsCamera = currentPlayer.currentCamera.get(); + if (currentPlayer.isCurrentCameraBuiltin()) { + soundsCamera = currentPlayer.fpCamera.get(); + } + bool relative = soundsCamera == currentPlayer.fpCamera.get(); + if (!relative) { + pos = currentPlayer.getPosition(); + } + audio::play( + sound, + pos, + relative, + 0.333f, + 1.0f + (rand() % 6 - 3) * 0.05f, + false, + audio::PRIORITY_LOW, + audio::get_channel_index("regular") + ); + }); + + controller.getBlocksController()->listenBlockInteraction( + [&level, &assets] + (auto player, const auto& pos, const auto& def, BlockInteraction type) { auto material = level.content.findBlockMaterial(def.material); if (material == nullptr) { return; } - if (type == BlockInteraction::step) { - auto sound = rassets.get(material->stepsSound); - glm::vec3 pos {}; - auto soundsCamera = currentPlayer->currentCamera.get(); - if (currentPlayer->isCurrentCameraBuiltin()) { - soundsCamera = currentPlayer->fpCamera.get(); - } - bool relative = player == currentPlayer && - soundsCamera == currentPlayer->fpCamera.get(); - if (!relative) { - pos = player->getPosition(); - } - audio::play( - sound, - pos, - relative, - 0.333f, - 1.0f + (rand() % 6 - 3) * 0.05f, - false, - audio::PRIORITY_LOW, - audio::get_channel_index("regular") - ); - } else { + if (type != BlockInteraction::step) { audio::Sound* sound = nullptr; switch (type) { case BlockInteraction::placing: - sound = rassets.get(material->placeSound); + sound = assets.get(material->placeSound); break; case BlockInteraction::destruction: - sound = rassets.get(material->breakSound); + sound = assets.get(material->breakSound); break; default: break; @@ -108,6 +144,6 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const { return *contentCache; } -LevelController* LevelFrontend::getController() const { +LevelController& LevelFrontend::getController() const { return controller; } diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp index cc4f996b2..451f88675 100644 --- a/src/frontend/LevelFrontend.hpp +++ b/src/frontend/LevelFrontend.hpp @@ -2,24 +2,24 @@ #include -class Level; class Assets; -class Player; -class Engine; class ContentGfxCache; +class Engine; +class Level; class LevelController; +class PlayerController; struct EngineSettings; class LevelFrontend { Level& level; - LevelController* controller; + LevelController& controller; Assets& assets; std::unique_ptr contentCache; public: LevelFrontend( Engine& engine, - Player* currentPlayer, - LevelController* controller, + PlayerController& currentPlayer, + LevelController& controller, const EngineSettings& settings ); ~LevelFrontend(); @@ -27,5 +27,5 @@ public: Level& getLevel(); const ContentGfxCache& getContentGfxCache() const; ContentGfxCache& getContentGfxCache(); - LevelController* getController() const; + LevelController& getController() const; }; diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 6f3593610..6a4afbd89 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -272,7 +272,7 @@ void Hud::updateWorldGenDebug() { const auto& chunks = *player.chunks; uint padding = engine.getSettings().chunks.padding.get(); auto generator = - frontend.getController()->getChunksController()->getGenerator(); + frontend.getController().getChunksController()->getGenerator(); auto debugInfo = generator->createDebugInfo(); int width = debugImgWorldGen->getWidth(); @@ -294,7 +294,7 @@ void Hud::updateWorldGenDebug() { bool isInLoadingZone = frontend.getController() - ->getChunksController() + .getChunksController() ->isInLoadingZone(player, padding, ax + ox, az + oz); data[(flippedZ * width + x) * 4 + 1] = diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 13618d33a..737742afd 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -68,7 +68,7 @@ LevelScreen::LevelScreen( ); frontend = std::make_unique( - engine, player, controller.get(), settings + engine, *playerController, *controller, settings ); renderer = std::make_unique( engine, *frontend, *player diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 35ea961ec..3d8c75872 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -229,26 +229,8 @@ PlayerController::PlayerController( } void PlayerController::onFootstep(const Hitbox& hitbox) { - const auto& pos = hitbox.position; - const auto& half = hitbox.getHalfSize(); - - for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { - for (int offsetX = -1; offsetX <= 1; offsetX++) { - int x = std::floor(pos.x + half.x * offsetX); - int y = std::floor(pos.y - half.y * 1.1f); - int z = std::floor(pos.z + half.z * offsetZ); - auto vox = player.chunks->get(x, y, z); - if (vox) { - auto& def = level.content.getIndices()->blocks.require(vox->id); - if (!def.obstacle) { - continue; - } - blocksController.onBlockInteraction( - &player, glm::ivec3(x, y, z), def, BlockInteraction::step - ); - return; - } - } + if (footstepCallback) { + footstepCallback(hitbox); } } @@ -557,3 +539,7 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta) Player& PlayerController::getPlayer() { return player; } + +void PlayerController::setFootstepCallback(FootstepCallback&& callback) { + footstepCallback = std::move(callback); +} diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp index e730ff37c..5ec8e4b50 100644 --- a/src/logic/PlayerController.hpp +++ b/src/logic/PlayerController.hpp @@ -18,6 +18,8 @@ struct Hitbox; struct CameraSettings; struct EngineSettings; +using FootstepCallback = std::function; + class CameraControl { Player& player; std::shared_ptr camera; @@ -57,6 +59,7 @@ class PlayerController { CameraControl camControl; BlocksController& blocksController; float interactionTimer = 0.0f; + FootstepCallback footstepCallback; void updateKeyboard(const Input& inputEvents); void resetKeyboard(); @@ -89,5 +92,8 @@ public: void postUpdate( float delta, int windowHeight, const Input* inputEvents, bool pause ); + Player& getPlayer(); + + void setFootstepCallback(FootstepCallback&& callback); }; From 8c06048b7e36cc0beed77ced0638bfd22645c08a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Mar 2026 21:38:53 +0300 Subject: [PATCH 36/73] fix --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index cfddcb783..bf78f860a 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -145,7 +145,7 @@ static bool calc_collision_neg_y( if (vel.y < 0.0f) { vel.y = 0.0f; if (hitbox.groundMaterial.empty()) { - hitbox.groundMaterial = hitbox.material; + hitbox.groundMaterial = box->material; } return true; } From c0a89cd93ae000091569534f171ced21b1b3b1df Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Mar 2026 21:39:59 +0300 Subject: [PATCH 37/73] add rigidbody:get_material, set_material --- res/modules/internal/stdcomp.lua | 2 ++ src/logic/scripting/lua/libs/lib__rigidbody.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index 7cedd1887..d85c11963 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -32,6 +32,8 @@ local Rigidbody = {__index={ set_crouching=function(self, b) return __rigidbody.set_crouching(self.eid, b) end, get_body_type=function(self) return __rigidbody.get_body_type(self.eid) end, set_body_type=function(self, s) return __rigidbody.set_body_type(self.eid, s) end, + get_material=function(self) return __rigidbody.get_material(self.eid) end, + set_material=function(self, s) return __rigidbody.set_material(self.eid, s) end, }} local function new_Rigidbody(eid) diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index f32e9d986..1e04e3b3c 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -150,6 +150,20 @@ static int l_set_linear_damping(lua::State* L) { return 0; } +static int l_set_material(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + entity->getRigidbody().hitbox.material = lua::require_string(L, 2); + } + return 0; +} + +static int l_get_material(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + return lua::pushstring(L, entity->getRigidbody().hitbox.material); + } + return 0; +} + const luaL_Reg rigidbodylib[] = { {"is_enabled", lua::wrap}, {"set_enabled", lua::wrap}, @@ -169,5 +183,7 @@ const luaL_Reg rigidbodylib[] = { {"set_crouching", lua::wrap}, {"get_body_type", lua::wrap}, {"set_body_type", lua::wrap}, + {"set_material", lua::wrap}, + {"get_material", lua::wrap}, {nullptr, nullptr} }; From 6f195c9a2ccf441017e092da81ba9315ad1e5021 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Mar 2026 21:42:53 +0300 Subject: [PATCH 38/73] update doc/*/scripting/ecs.md --- doc/en/scripting/ecs.md | 5 +++++ doc/ru/scripting/ecs.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 7da294c22..79289cbe5 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -114,6 +114,11 @@ body:set_crouching(enabled: bool) body:get_body_type() -> str -- Sets the physical body type body:set_body_type(type: str) + +-- Returns the body material (same as for blocks) +body:get_material() -> str +-- Sets the body material +body:set_material(material: str) ``` ### Skeleton diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index 6c1b041d7..4bc13bc31 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -114,6 +114,11 @@ body:set_crouching(enabled: bool) body:get_body_type() -> str -- Устанавливает тип физического тела body:set_body_type(type: str) + +-- Возвращает материал тела (то же, что и у блоков) +body:get_material() -> str +-- Устанавливает материал тела +body:set_material(material: str) ``` ### Skeleton From 7d09a7957c2b778c5d7a5d989ca7510d13c58a31 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 6 Mar 2026 19:47:43 +0300 Subject: [PATCH 39/73] fix division by zero in some cases --- src/objects/Entities.cpp | 26 +++++++++++++++++--------- src/physics/PhysicsSolver.cpp | 9 +++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 6750116df..17b5acd83 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -1,28 +1,29 @@ #define VC_ENABLE_REFLECTION #include "Entities.hpp" -#include -#include -#include - #include "assets/Assets.hpp" #include "content/Content.hpp" #include "data/dv_util.hpp" #include "debug/Logger.hpp" #include "engine/Engine.hpp" +#include "Entity.hpp" +#include "EntityDef.hpp" +#include "graphics/commons/Model.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/LineBatch.hpp" -#include "graphics/commons/Model.hpp" #include "graphics/render/ModelBatch.hpp" #include "logic/scripting/scripting.hpp" #include "maths/FrustumCulling.hpp" #include "maths/rays.hpp" -#include "EntityDef.hpp" -#include "Entity.hpp" -#include "rigging.hpp" +#include "maths/util.hpp" #include "physics/PhysicsSolver.hpp" +#include "rigging.hpp" #include "world/Level.hpp" +#include +#include +#include + static debug::Logger logger("entities"); Entities::Entities(Level& level) @@ -336,7 +337,14 @@ void Entities::updatePhysics(float delta) { ? 8.0f : (!grounded ? 2.0f : 10.0f); hitbox.scale = transform.size; - transform.setPos(hitbox.position); + if (util::is_nan_or_inf(hitbox.position)) { + logger.error() + << "something bad happened in physics engine (entity " + << eid.def.name << "#" << eid.uid << ")"; + hitbox.position = transform.pos; + } else { + transform.setPos(hitbox.position); + } if (hitbox.grounded && !grounded) { scripting::on_entity_grounded( *get(eid.uid), glm::length(prevVel - hitbox.velocity) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index bf78f860a..f9e4b6e23 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -140,8 +140,10 @@ static bool calc_collision_neg_y( if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } - hitbox.groundVelocity = - (box->position - box->prevPosition) / box->delta; + if (glm::abs(box->delta) > 1e-5f) { + hitbox.groundVelocity = + (box->position - box->prevPosition) / box->delta; + } if (vel.y < 0.0f) { vel.y = 0.0f; if (hitbox.groundMaterial.empty()) { @@ -300,6 +302,9 @@ void PhysicsSolver::calcSubstep( ) { if (hitbox.grounded) { pos.x += hitbox.groundVelocity.x * dt * substeps; + if (hitbox.groundVelocity.y < 0.0f) { + pos.y += hitbox.groundVelocity.y * dt * substeps; + } pos.z += hitbox.groundVelocity.z * dt * substeps; } From 5304223e9543ebcd341411aca885522ce17599c9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Mar 2026 11:55:32 +0300 Subject: [PATCH 40/73] add systems availability checks to block, cameras, world libraries --- src/logic/scripting/lua/libs/libblock.cpp | 156 ++++++++++++++------- src/logic/scripting/lua/libs/libcamera.cpp | 24 +++- src/logic/scripting/lua/libs/libworld.cpp | 10 +- 3 files changed, 131 insertions(+), 59 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 2fa2b1281..f2a615dc0 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -20,8 +20,22 @@ using namespace scripting; -static inline const Block* get_block_def(lua::State* L) { - auto indices = content->getIndices(); +static const Content& require_content() { + if (content == nullptr) { + throw std::runtime_error("content is not initialized"); + } + return *content; +} + +static Level& require_level() { + if (level == nullptr) { + throw std::runtime_error("level is not initialized"); + } + return *level; +} + +static const Block* get_block_def(lua::State* L) { + auto indices = require_content().getIndices(); auto id = lua::tointeger(L, 1); return indices->blocks.get(id); } @@ -45,7 +59,7 @@ static int l_is_solid_at(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_solid_at(*level->chunks, x, y, z) + L, blocks_agent::is_solid_at(*require_level().chunks, x, y, z) ); } @@ -55,7 +69,7 @@ static int l_count(lua::State* L) { static int l_index(lua::State* L) { auto name = lua::require_string(L, 1); - return lua::pushinteger(L, content->blocks.require(name).rt.id); + return lua::pushinteger(L, require_content().blocks.require(name).rt.id); } static int l_is_extended(lua::State* L) { @@ -76,7 +90,7 @@ static int l_is_segment(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunks, x, y, z); + const auto& vox = blocks_agent::require(*require_level().chunks, x, y, z); return lua::pushboolean(L, vox.state.segment); } @@ -84,10 +98,11 @@ static int l_seek_origin(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunks, x, y, z); + const auto& level = require_level(); + const auto& vox = blocks_agent::require(*level.chunks, x, y, z); auto& def = indices->blocks.require(vox.id); return lua::pushivec_stack( - L, blocks_agent::seek_origin(*level->chunks, {x, y, z}, def, vox.state) + L, blocks_agent::seek_origin(*level.chunks, {x, y, z}, def, vox.state) ); } @@ -98,10 +113,12 @@ static int l_set(lua::State* L) { auto id = lua::tointeger(L, 4); auto state = lua::tointeger(L, 5); bool noupdate = lua::toboolean(L, 6); - if (static_cast(id) >= indices->blocks.count()) { + auto& level = require_level(); + auto& indices = require_content().getIndices()->blocks; + if (static_cast(id) >= indices.count()) { return 0; } - if (!blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state))) { + if (!blocks_agent::set(*level.chunks, x, y, z, id, int2blockstate(state))) { return 0; } @@ -123,7 +140,7 @@ static int l_get(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto vox = blocks_agent::get(*require_level().chunks, x, y, z); int id = vox == nullptr ? -1 : vox->id; return lua::pushinteger(L, id); } @@ -138,8 +155,9 @@ template static int get_axis(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); + auto& level = require_level(); if (lua::gettop(L) == 2) { - const auto& def = level->content.getIndices()->blocks.require(x); + const auto& def = level.content.getIndices()->blocks.require(x); return get_axis(L, def, y); } auto z = lua::tointeger(L, 3); @@ -147,11 +165,11 @@ static int get_axis(lua::State* L) { glm::ivec3 defAxis {}; defAxis[n] = 1; - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto vox = blocks_agent::get(*level.chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, defAxis); } - const auto& def = level->content.getIndices()->blocks.require(vox->id); + const auto& def = level.content.getIndices()->blocks.require(vox->id); if (!def.rotatable) { return lua::pushivec_stack(L, defAxis); } else { @@ -175,7 +193,7 @@ static int l_get_rotation(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto vox = blocks_agent::get(*require_level().chunks, x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -185,7 +203,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - blocks_agent::set_rotation(*level->chunks, x, y, z, value); + blocks_agent::set_rotation(*require_level().chunks, x, y, z, value); return 0; } @@ -193,7 +211,7 @@ static int l_get_states(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto vox = blocks_agent::get(*require_level().chunks, x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } @@ -208,7 +226,7 @@ static int l_set_states(lua::State* L) { } int cx = floordiv(x); int cz = floordiv(z); - auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); + auto chunk = blocks_agent::get_chunk(*require_level().chunks, cx, cz); if (chunk == nullptr) { return 0; } @@ -227,16 +245,17 @@ static int l_get_user_bits(lua::State* L) { auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; auto bits = lua::tointeger(L, 5); - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto& level = require_level(); + auto vox = blocks_agent::get(*level.chunks, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = level.content.getIndices()->blocks.require(vox->id); if (def.rt.extended) { auto origin = blocks_agent::seek_origin( - *level->chunks, {x, y, z}, def, vox->state + *level.chunks, {x, y, z}, def, vox->state ); - vox = blocks_agent::get(*level->chunks, origin.x, origin.y, origin.z); + vox = blocks_agent::get(*level.chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -246,7 +265,9 @@ static int l_get_user_bits(lua::State* L) { } static int l_get_variant(lua::State* L) { - auto& chunks = *level->chunks; + auto& level = require_level(); + auto& chunks = *level.chunks; + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -255,7 +276,7 @@ static int l_get_variant(lua::State* L) { if (vox == nullptr) { return lua::pushinteger(L, 0); } - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = level.content.getIndices()->blocks.require(vox->id); if (def.variants == nullptr) { return lua::pushinteger(L, 0); } @@ -272,7 +293,9 @@ static int l_get_variant(lua::State* L) { } static int l_set_user_bits(lua::State* L) { - auto& chunks = *level->chunks; + auto& level = require_level(); + auto& chunks = *level.chunks; + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -291,7 +314,7 @@ static int l_set_user_bits(lua::State* L) { int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; auto vox = &chunk->voxels[vox_index(lx, y, lz)]; - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = level.content.getIndices()->blocks.require(vox->id); if (def.rt.extended) { auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z); @@ -313,7 +336,9 @@ static int l_set_user_bits(lua::State* L) { } static int l_set_variant(lua::State* L) { - auto& chunks = *level->chunks; + auto& level = require_level(); + auto& chunks = *level.chunks; + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -327,7 +352,7 @@ static int l_set_variant(lua::State* L) { int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; auto vox = &chunk->voxels[vox_index(lx, y, lz)]; - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = level.content.getIndices()->blocks.require(vox->id); if (def.variants == nullptr) { return 0; @@ -358,11 +383,12 @@ static int l_set_variant(lua::State* L) { } static int l_is_replaceable_at(lua::State* L) { + auto& level = require_level(); auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_replaceable_at(*level->chunks, x, y, z) + L, blocks_agent::is_replaceable_at(*level.chunks, x, y, z) ); } @@ -373,20 +399,28 @@ static int l_caption(lua::State* L) { return 0; } -static lua::Integer get_variant_index(lua::State* L, const Block* const block, int argumentIndex) { - const auto variantIndex = lua::gettop(L) >= argumentIndex ? lua::tointeger(L, argumentIndex) : 0; - const size_t variantsSize = block->variants->variants.size(); +static lua::Integer get_variant_index( + lua::State* L, const Block& block, int argumentIndex +) { + const auto variantIndex = + lua::isnumber(L, argumentIndex) ? lua::tointeger(L, argumentIndex) : 0; + const size_t variantsSize = block.variants->variants.size(); if (variantIndex < 0 || variantIndex >= variantsSize) { throw std::out_of_range( - "variant index out of bounds [0, " + std::to_string(variantsSize - 1) + "]"); + "variant index out of bounds [0, " + + std::to_string(variantsSize - 1) + "]" + ); } return variantIndex; } static int l_get_textures(lua::State* L) { if (auto def = get_block_def(L)) { - const auto& textureFaces = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].textureFaces : - def->defaults.textureFaces); + const auto& textureFaces = + (def->variants + ? def->variants->variants[get_variant_index(L, *def, 2)] + .textureFaces + : def->defaults.textureFaces); lua::createtable(L, 6, 0); for (size_t i = 0; i < 6; i++) { lua::pushstring(L, textureFaces[i]); @@ -397,11 +431,13 @@ static int l_get_textures(lua::State* L) { return 0; } - static int l_model_name(lua::State* L) { if (auto def = get_block_def(L)) { - const auto& modelName = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.name : - def->defaults.model.name); + const auto& modelName = + (def->variants + ? def->variants->variants[get_variant_index(L, *def, 2)] + .model.name + : def->defaults.model.name); if (modelName.empty()) { return lua::pushlstring(L, def->name + ".model"); } @@ -412,8 +448,11 @@ static int l_model_name(lua::State* L) { static int l_get_model(lua::State* L) { if (auto def = get_block_def(L)) { - const BlockModelType modelType = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.type : - def->defaults.model.type); + const BlockModelType modelType = + (def->variants + ? def->variants->variants[get_variant_index(L, *def, 2)] + .model.type + : def->defaults.model.type); return lua::pushlstring(L, BlockModelTypeMeta.getName(modelType)); } return 0; @@ -422,7 +461,8 @@ static int l_get_model(lua::State* L) { static int l_get_hitbox(lua::State* L) { if (auto def = get_block_def(L)) { size_t rotation = lua::tointeger(L, 2); - const size_t hitboxIndex = static_cast(lua::gettop(L) >= 3 ? lua::tointeger(L, 3) : 0); + const size_t hitboxIndex = + static_cast(lua::isnumber(L, 3) ? lua::tointeger(L, 3) : 0); if (def->rotatable) { rotation %= def->rotations.MAX_COUNT; } else { @@ -456,25 +496,28 @@ static int l_get_picking_item(lua::State* L) { } static int l_place(lua::State* L) { + auto& level = require_level(); + auto& indices = *level.content.getIndices(); + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto id = lua::tointeger(L, 4); auto state = lua::tointeger(L, 5); auto playerid = lua::gettop(L) >= 6 ? lua::tointeger(L, 6) : -1; - if (static_cast(id) >= indices->blocks.count()) { + if (static_cast(id) >= indices.blocks.count()) { return 0; } - if (!blocks_agent::get(*level->chunks, x, y, z)) { + if (!blocks_agent::get(*level.chunks, x, y, z)) { return 0; } - const auto def = level->content.getIndices()->blocks.get(id); + const auto def = indices.blocks.get(id); if (def == nullptr) { throw std::runtime_error( "there is no block with index " + std::to_string(id) ); } - auto player = level->players->get(playerid); + auto player = level.players->get(playerid); controller->getBlocksController()->placeBlock( player, *def, int2blockstate(state), x, y, z ); @@ -482,21 +525,26 @@ static int l_place(lua::State* L) { } static int l_destruct(lua::State* L) { + auto& level = require_level(); + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1; - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto playerid = lua::isnumber(L, 4) ? lua::tointeger(L, 4) : -1; + + auto vox = blocks_agent::get(*level.chunks, x, y, z); if (vox == nullptr) { return 0; } - auto& def = level->content.getIndices()->blocks.require(vox->id); - auto player = level->players->get(playerid); + auto& def = level.content.getIndices()->blocks.require(vox->id); + auto player = level.players->get(playerid); controller->getBlocksController()->breakBlock(player, def, x, y, z); return 0; } static int l_raycast(lua::State* L) { + auto& level = require_level(); + auto start = lua::tovec<3>(L, 1); auto dir = lua::tovec<3>(L, 2); auto maxDistance = lua::tonumber(L, 3); @@ -526,7 +574,7 @@ static int l_raycast(lua::State* L) { glm::ivec3 normal; glm::ivec3 iend; if (auto voxel = blocks_agent::raycast( - *level->chunks, + *level.chunks, start, dir, maxDistance, @@ -620,6 +668,8 @@ static int get_field( } static int l_get_field(lua::State* L) { + auto& level = require_level(); + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -628,9 +678,10 @@ static int l_get_field(lua::State* L) { if (lua::gettop(L) >= 5) { index = lua::tointeger(L, 5); } + auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); + auto chunk = blocks_agent::get_chunk(*level.chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } @@ -689,6 +740,8 @@ static int set_field( } static int l_set_field(lua::State* L) { + auto& level = require_level(); + auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -698,11 +751,12 @@ static int l_set_field(lua::State* L) { if (lua::gettop(L) >= 6) { index = lua::tointeger(L, 6); } + auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; - auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); + auto chunk = blocks_agent::get_chunk(*level.chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } diff --git a/src/logic/scripting/lua/libs/libcamera.cpp b/src/logic/scripting/lua/libs/libcamera.cpp index 6ec6ef96c..c45d2740a 100644 --- a/src/logic/scripting/lua/libs/libcamera.cpp +++ b/src/logic/scripting/lua/libs/libcamera.cpp @@ -7,28 +7,42 @@ using namespace scripting; +static Level& require_level() { + if (level == nullptr) { + throw std::runtime_error("level is not initialized"); + } + return *level; +} + +static const Content& require_content() { + if (content == nullptr) { + throw std::runtime_error("content is not initialized"); + } + return *content; +} + template static int l_camera_getter(lua::State* L) { size_t index = static_cast(lua::tointeger(L, 1)); - return getterfunc(L, *level->cameras.at(index)); + return getterfunc(L, *require_level().cameras.at(index)); } template static int l_camera_setter(lua::State* L) { size_t index = static_cast(lua::tointeger(L, 1)); - setterfunc(L, *level->cameras.at(index), 2); + setterfunc(L, *require_level().cameras.at(index), 2); return 0; } static int l_index(lua::State* L) { auto name = lua::require_string(L, 1); - auto& indices = content->getIndices(ResourceType::CAMERA); + auto& indices = require_content().getIndices(ResourceType::CAMERA); return lua::pushinteger(L, indices.indexOf(name)); } static int l_name(lua::State* L) { size_t index = static_cast(lua::tointeger(L, 1)); - auto& indices = content->getIndices(ResourceType::CAMERA); + auto& indices = require_content().getIndices(ResourceType::CAMERA); return lua::pushstring(L, indices.getName(index)); } @@ -89,7 +103,7 @@ static int getter_up(lua::State* L, const Camera& camera) { static int l_look_at(lua::State* L) { size_t index = static_cast(lua::tointeger(L, 1)); - auto& camera = *level->cameras.at(index); + auto& camera = *require_level().cameras.at(index); auto center = lua::tovec<3>(L, 2); auto matrix = glm::inverse( glm::lookAt(glm::vec3(), center - camera.position, glm::vec3(0, 1, 0)) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 30a619788..c2d6be43c 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -25,11 +25,15 @@ using namespace scripting; namespace fs = std::filesystem; -static WorldInfo& require_world_info() { +static Level& require_level() { if (level == nullptr) { - throw std::runtime_error("no world open"); + throw std::runtime_error("world is not open"); } - return level->getWorld()->getInfo(); + return *level; +} + +static WorldInfo& require_world_info() { + return require_level().getWorld()->getInfo(); } static int l_is_open(lua::State* L) { From 0fe0cc5829de915cf105ac4ec3ed4f5440efbfef Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 12:39:16 +0300 Subject: [PATCH 41/73] fix: sensors list not clearing --- src/objects/Entities.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 17b5acd83..0ce9a05f3 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -287,6 +287,7 @@ void Entities::preparePhysics(float delta) { auto parts = sensorsTickClock.getParts(); auto& sensors = physics.getSensorsWriteable(); + sensors.clear(); auto view = registry->view(); for (auto [entity, eid, transform, rigidbody] : view.each()) { @@ -339,7 +340,7 @@ void Entities::updatePhysics(float delta) { hitbox.scale = transform.size; if (util::is_nan_or_inf(hitbox.position)) { logger.error() - << "something bad happened in physics engine (entity " + << "physics simulation produced nan or inf (entity " << eid.def.name << "#" << eid.uid << ")"; hitbox.position = transform.pos; } else { From a8dd5e9cb326863db09273d7f30caea97daa4923 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 16:35:28 +0300 Subject: [PATCH 42/73] fix side collision position correction --- src/objects/Entities.cpp | 3 ++- src/physics/PhysicsSolver.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 0ce9a05f3..c81b01a38 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -320,7 +320,8 @@ void Entities::updatePhysics(float delta) { for (int solid = false; solid <= true; solid++) { for (auto [entity, eid, transform, rigidbody] : view.each()) { - if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) { + if (!rigidbody.enabled || + rigidbody.hitbox.type == BodyType::STATIC) { continue; } if (eid.def.solid == solid) { diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index f9e4b6e23..823871f2b 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -12,7 +12,8 @@ #define GLM_ENABLE_EXPERIMENTAL #include -inline const float E = 0.03f; +inline constexpr float E = 0.03f; +inline constexpr float MAX_FIX = 0.15f; static debug::Logger logger("physics-solver"); @@ -78,7 +79,7 @@ static void calc_collision( } else { continue; } - if ((pos[nx] - newx) * sign > 0.0f) { + if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) { if (vel[nx] * sign > 0.0f) { vel[nx] = 0.0f; } @@ -137,7 +138,7 @@ static bool calc_collision_neg_y( auto boxhalf = box->getHalfSize(); if (box->position.y < pos.y && box->getAABB().intersects(aabb)) { float newy = box->position.y + boxhalf.y + half.y; - if (pos.y < newy && glm::abs(pos.y - newy) < 0.5f) { + if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) { pos.y = newy; } if (glm::abs(box->delta) > 1e-5f) { @@ -210,7 +211,7 @@ void PhysicsSolver::calcCollisions( if (calc_collision_neg_y(hitbox, chunks, solidHitboxes, half)) { hitbox.grounded = true; } - + if (vel.y > 0.0f){ AABB boxAABB = AABB(-half, +half); boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f + E * 2, 1.0f - E * 4)); From b49e2106b36a9e5228cee6edd8f1f6084184fff8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 16:46:12 +0300 Subject: [PATCH 43/73] update libitem.cpp --- src/logic/scripting/lua/libs/libitem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logic/scripting/lua/libs/libitem.cpp b/src/logic/scripting/lua/libs/libitem.cpp index 3a0f77ec7..4c7ba3983 100644 --- a/src/logic/scripting/lua/libs/libitem.cpp +++ b/src/logic/scripting/lua/libs/libitem.cpp @@ -8,6 +8,9 @@ using namespace scripting; static const ItemDef* get_item_def(lua::State* L, int idx) { + if (content == nullptr) { + throw std::runtime_error("content is not initialized"); + } auto indices = content->getIndices(); auto id = lua::tointeger(L, idx); return indices->items.get(id); From 68ef9740969c4102370f74335993ca6125488456 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 16:51:24 +0300 Subject: [PATCH 44/73] update libplayer.cpp --- src/logic/scripting/lua/libs/libplayer.cpp | 31 +++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/logic/scripting/lua/libs/libplayer.cpp b/src/logic/scripting/lua/libs/libplayer.cpp index 9f634bddb..a978e5c22 100644 --- a/src/logic/scripting/lua/libs/libplayer.cpp +++ b/src/logic/scripting/lua/libs/libplayer.cpp @@ -14,6 +14,13 @@ using namespace scripting; +static Level& require_level() { + if (level == nullptr) { + throw std::runtime_error("world is not loaded"); + } + return *level; +} + inline Player* get_player(lua::State* L, int idx) { if (!lua::isnumber(L, idx)) { if (engine->isHeadless()) { @@ -22,7 +29,7 @@ inline Player* get_player(lua::State* L, int idx) { ); } } - return level->players->get(lua::tointeger(L, idx)); + return require_level().players->get(lua::tointeger(L, idx)); } static int l_get_pos(lua::State* L) { @@ -304,15 +311,18 @@ static int l_create(lua::State* L) { if (lua::gettop(L) >= 2) { playerId = lua::tointeger(L, 2); } - auto player = level->players->create(playerId); + auto& level = require_level(); + auto player = level.players->create(playerId); player->setName(lua::require_string(L, 1)); return lua::pushinteger(L, player->getId()); } static int l_delete(lua::State* L) { auto id = lua::tointeger(L, 1); - level->players->suspend(id); - level->players->remove(id); + + auto& level = require_level(); + level.players->suspend(id); + level.players->remove(id); return 0; } @@ -324,19 +334,21 @@ static int l_is_suspended(lua::State* L) { } static int l_set_suspended(lua::State* L) { + auto& level = require_level(); if (lua::toboolean(L, 2)) { - level->players->suspend(lua::tointeger(L, 1)); + level.players->suspend(lua::tointeger(L, 1)); } else { - level->players->resume(lua::tointeger(L, 1)); + level.players->resume(lua::tointeger(L, 1)); } return 0; } static int l_get_all_in_radius(lua::State* L) { + auto& level = require_level(); auto center = lua::tovec3(L, 1); auto radius = static_cast(lua::tonumber(L, 2)); - auto players = level->players->getAllInRadius(center, radius); + auto players = level.players->getAllInRadius(center, radius); lua::createtable(L, players.size(), 0); for (size_t i = 0; i < players.size(); i++) { lua::pushinteger(L, players[i]->getId()); @@ -346,7 +358,7 @@ static int l_get_all_in_radius(lua::State* L) { } static int l_get_all(lua::State* L) { - auto players = level->players->getAll(); + auto players = require_level().players->getAll(); lua::createtable(L, players.size(), 0); for (size_t i = 0; i < players.size(); i++) { lua::pushinteger(L, players[i]->getId()); @@ -356,8 +368,9 @@ static int l_get_all(lua::State* L) { } static int l_get_nearest(lua::State* L) { + auto& level = require_level(); auto position = lua::tovec3(L, 1); - if (auto player = level->players->getNearest(position)) { + if (auto player = level.players->getNearest(position)) { lua::pushinteger(L, player->getId()); return 1; } From 068e546a63dfd9468a436d0e711d8f38b86c3f10 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 17:51:56 +0300 Subject: [PATCH 45/73] update libinventory.cpp --- src/logic/scripting/lua/libs/libinventory.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/logic/scripting/lua/libs/libinventory.cpp b/src/logic/scripting/lua/libs/libinventory.cpp index 7f43ae88b..3ec9d2ca3 100644 --- a/src/logic/scripting/lua/libs/libinventory.cpp +++ b/src/logic/scripting/lua/libs/libinventory.cpp @@ -8,14 +8,28 @@ using namespace scripting; namespace { + Level& require_level() { + if (level == nullptr) { + throw std::runtime_error("world is not open"); + } + return *level; + } + + const Content& require_content() { + if (content == nullptr) { + throw std::runtime_error("content is not initialized"); + } + return *content; + } + void validate_itemid(itemid_t id) { - if (id >= indices->items.count()) { + if (id >= require_content().getIndices()->items.count()) { throw std::runtime_error("invalid item id"); } } Inventory& get_inventory(int64_t id) { - auto inv = level->inventories->get(id); + auto inv = require_level().inventories->get(id); if (inv == nullptr) { throw std::runtime_error("inventory not found: " + std::to_string(id)); } @@ -23,7 +37,7 @@ namespace { } Inventory& get_inventory(int64_t id, int arg) { - auto inv = level->inventories->get(id); + auto inv = require_level().inventories->get(id); if (inv == nullptr) { throw std::runtime_error( "inventory not found: " + std::to_string(id) + " argument " + @@ -137,7 +151,7 @@ static int l_unbind_block(lua::State* L) { static int l_create(lua::State* L) { auto invsize = lua::tointeger(L, 1); - auto inv = level->inventories->create(invsize); + auto inv = require_level().inventories->create(invsize); if (inv == nullptr) { return lua::pushinteger(L, 0); } @@ -146,13 +160,13 @@ static int l_create(lua::State* L) { static int l_remove(lua::State* L) { auto invid = lua::tointeger(L, 1); - level->inventories->remove(invid); + require_level().inventories->remove(invid); return 0; } static int l_clone(lua::State* L) { auto id = lua::tointeger(L, 1); - auto clone = level->inventories->clone(id); + auto clone = require_level().inventories->clone(id); if (clone == nullptr) { return lua::pushinteger(L, 0); } From 794ce07bd5409b7643d8fdd200adf3c3279095dc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Mar 2026 22:47:41 +0300 Subject: [PATCH 46/73] fix side collision position correction for blocks --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 823871f2b..c9947c299 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -106,7 +106,7 @@ static void calc_collision( if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { float newx = std::floor(coord[nx]) - half[nx] * sign + (sign > 0 ? obstacle->min() : obstacle->max())[nx]; - if ((pos[nx] - newx) * sign > 0.0f) { + if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) { vel[nx] = 0.0f; pos[nx] = newx; } From 4b4f2439c0110367e197ac94f6219a8f8421eb95 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Mar 2026 22:08:40 +0300 Subject: [PATCH 47/73] add more reason to physics simulation substeps --- src/objects/Entities.cpp | 72 +++++++++----------- src/physics/Hitbox.cpp | 17 +++-- src/physics/Hitbox.hpp | 12 ++-- src/physics/PhysicsSolver.cpp | 122 ++++++++++++++++++---------------- src/physics/PhysicsSolver.hpp | 10 +-- 5 files changed, 119 insertions(+), 114 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index c81b01a38..259e9e3c6 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -80,7 +80,7 @@ entityid_t Entities::spawn( entities[id] = entity; uids[entity] = id; - registry->emplace(entity, static_cast(id), def); + registry->emplace(entity, id, def); const auto& tsf = registry->emplace( entity, position, @@ -92,7 +92,7 @@ entityid_t Entities::spawn( auto& body = registry->emplace( entity, true, - Hitbox {def.bodyType, position, def.hitbox * 0.5f}, + Hitbox {id, def.bodyType, position, def.hitbox * 0.5f}, std::vector {} ); body.initialize(def, id, *this); @@ -280,6 +280,7 @@ void Entities::updateSensors( void Entities::preparePhysics(float delta) { auto& physics = *level.physics; + auto& hitboxes = physics.getHitboxesWriteable(); auto& solidHitboxes = physics.getSolidHitboxesWriteable(); if (sensorsTickClock.update(delta)) { @@ -301,11 +302,16 @@ void Entities::preparePhysics(float delta) { } } + hitboxes.clear(); solidHitboxes.clear(); auto view = registry->view(); for (auto [entity, eid, rigidbody] : view.each()) { - if (!eid.def.solid || eid.destroyFlag || !rigidbody.enabled) { + if (eid.destroyFlag || !rigidbody.enabled) { + continue; + } + hitboxes.emplace_back(&rigidbody.hitbox); + if (!eid.def.solid) { continue; } solidHitboxes.emplace_back(&rigidbody.hitbox); @@ -318,43 +324,31 @@ void Entities::updatePhysics(float delta) { auto view = registry->view(); auto physics = level.physics.get(); - for (int solid = false; solid <= true; solid++) { - for (auto [entity, eid, transform, rigidbody] : view.each()) { - if (!rigidbody.enabled || - rigidbody.hitbox.type == BodyType::STATIC) { - continue; - } - if (eid.def.solid == solid) { - continue; - } - auto& hitbox = rigidbody.hitbox; - auto prevVel = hitbox.velocity; - bool grounded = hitbox.grounded; + int substeps = std::max(std::min(delta * 1000, 200), 8); + physics->step(*level.chunks, delta, substeps); - float vel = glm::length(prevVel); - int substeps = static_cast(delta * (vel + 2.0f) * 20); - substeps = std::min(100, std::max(2, substeps)); - physics->step(*level.chunks, hitbox, delta, substeps, eid.uid); - hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f) - ? 8.0f - : (!grounded ? 2.0f : 10.0f); - hitbox.scale = transform.size; - if (util::is_nan_or_inf(hitbox.position)) { - logger.error() - << "physics simulation produced nan or inf (entity " - << eid.def.name << "#" << eid.uid << ")"; - hitbox.position = transform.pos; - } else { - transform.setPos(hitbox.position); - } - if (hitbox.grounded && !grounded) { - scripting::on_entity_grounded( - *get(eid.uid), glm::length(prevVel - hitbox.velocity) - ); - } - if (!hitbox.grounded && grounded) { - scripting::on_entity_fall(*get(eid.uid)); - } + for (auto [entity, eid, transform, rigidbody] : view.each()) { + if (!rigidbody.enabled || + rigidbody.hitbox.type == BodyType::STATIC) { + continue; + } + auto& hitbox = rigidbody.hitbox; + hitbox.scale = transform.size; + if (util::is_nan_or_inf(hitbox.position)) { + logger.error() + << "physics simulation produced nan or inf (entity " + << eid.def.name << "#" << eid.uid << ")"; + hitbox.position = transform.pos; + } else { + transform.setPos(hitbox.position); + } + if (hitbox.grounded && !hitbox.prevGrounded) { + scripting::on_entity_grounded( + *get(eid.uid), glm::length(hitbox.prevVelocity - hitbox.velocity) + ); + } + if (!hitbox.grounded && hitbox.prevGrounded) { + scripting::on_entity_fall(*get(eid.uid)); } } } diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp index a72bdd939..e9e18ce58 100644 --- a/src/physics/Hitbox.cpp +++ b/src/physics/Hitbox.cpp @@ -2,10 +2,13 @@ #include -Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize) - : type(type), - position(position), - halfsize(halfsize), - velocity(0.0f,0.0f,0.0f), - prevPosition(position) -{} +Hitbox::Hitbox( + entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize +) + : entity(entity), + type(type), + position(position), + halfsize(halfsize), + velocity(0.0f, 0.0f, 0.0f), + prevPosition(position) { +} diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 15b7acc89..9d2213572 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -51,6 +51,7 @@ VC_ENUM_METADATA(BodyType) VC_ENUM_END struct Hitbox { + entityid_t entity; BodyType type; glm::vec3 position; glm::vec3 halfsize; @@ -65,17 +66,18 @@ struct Hitbox { bool crouching = false; float stepHeight = 0.5f; std::string material; - - glm::vec3 groundVelocity {}; std::string groundMaterial; + glm::vec3 groundVelocity {}; - // garbage tbh glm::vec3 prevPosition {}; - float delta = 0.0f; + glm::vec3 prevVelocity {}; + bool prevGrounded = false; static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f; - Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize); + Hitbox( + entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize + ); AABB getAABB() const { return AABB(position - halfsize, position + halfsize); diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index c9947c299..cf44024f1 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -141,13 +141,10 @@ static bool calc_collision_neg_y( if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) { pos.y = newy; } - if (glm::abs(box->delta) > 1e-5f) { - hitbox.groundVelocity = - (box->position - box->prevPosition) / box->delta; - } + hitbox.groundVelocity = box->position - box->prevPosition; if (vel.y < 0.0f) { vel.y = 0.0f; - if (hitbox.groundMaterial.empty()) { + if (hitbox.groundMaterial.empty() && !box->material.empty()) { hitbox.groundMaterial = box->material; } return true; @@ -297,16 +294,15 @@ void PhysicsSolver::calcSubstep( Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, - bool prevGrounded, float dt, int substeps ) { - if (hitbox.grounded) { - pos.x += hitbox.groundVelocity.x * dt * substeps; + if (glm::length2(hitbox.groundVelocity) > 1e-6f) { + pos.x += hitbox.groundVelocity.x; if (hitbox.groundVelocity.y < 0.0f) { - pos.y += hitbox.groundVelocity.y * dt * substeps; + pos.y += hitbox.groundVelocity.y; } - pos.z += hitbox.groundVelocity.z * dt * substeps; + pos.z += hitbox.groundVelocity.z; } auto initpos = pos; @@ -320,7 +316,7 @@ void PhysicsSolver::calcSubstep( vel, pos, half, - (prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f + (hitbox.prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f ); } @@ -374,64 +370,72 @@ void PhysicsSolver::calcSubstep( void PhysicsSolver::step( const GlobalChunks& chunks, - Hitbox& hitbox, float delta, - uint substeps, - entityid_t entity + uint substeps ) { - hitbox.prevPosition = hitbox.position; - hitbox.delta = delta; - hitbox.groundMaterial.clear(); + for (auto hitbox : hitboxes) { + hitbox->groundMaterial.clear(); + hitbox->prevGrounded = hitbox->grounded; + hitbox->grounded = false; + hitbox->prevVelocity = hitbox->velocity; + } float dt = delta / static_cast(substeps); - float linearDamping = hitbox.linearDamping * hitbox.friction; - - glm::vec3& pos = hitbox.position; - glm::vec3& vel = hitbox.velocity; - - bool prevGrounded = hitbox.grounded; - hitbox.grounded = false; for (uint i = 0; i < substeps; i++) { - calcSubstep(chunks, hitbox, vel, pos, prevGrounded, dt, substeps); - } - - vel.x /= 1.0f + delta * linearDamping; - vel.z /= 1.0f + delta * linearDamping; - if (hitbox.verticalDamping > 0.0f) { - vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping; - } - if (prevGrounded && !hitbox.grounded) { - auto appliedVelocity = hitbox.groundVelocity; - vel += appliedVelocity; - hitbox.groundVelocity = {}; - } - - AABB aabb; - aabb.a = pos - hitbox.getHalfSize(); - aabb.b = pos + hitbox.getHalfSize(); - for (size_t i = 0; i < sensors.size(); i++) { - auto& sensor = *sensors[i]; - if (sensor.entity == entity) { - continue; + for (auto hitbox : hitboxes) { + glm::vec3& pos = hitbox->position; + glm::vec3& vel = hitbox->velocity; + hitbox->prevPosition = hitbox->position; + calcSubstep(chunks, *hitbox, vel, pos, dt, substeps); } + } - bool triggered = false; - switch (sensor.type) { - case SensorType::AABB: - triggered = aabb.intersects(sensor.calculated.aabb); - break; - case SensorType::RADIUS: - triggered = glm::distance2( - pos, glm::vec3(sensor.calculated.radial)) - < sensor.calculated.radial.w; - break; + for (auto hitbox : hitboxes) { + float linearDamping = hitbox->linearDamping * hitbox->friction; + glm::vec3& pos = hitbox->position; + glm::vec3& vel = hitbox->velocity; + + vel.x /= 1.0f + delta * linearDamping; + vel.z /= 1.0f + delta * linearDamping; + if (hitbox->verticalDamping > 0.0f) { + vel.y /= 1.0f + delta * linearDamping * hitbox->verticalDamping; } - if (triggered) { - if (sensor.prevEntered.find(entity) == sensor.prevEntered.end()) { - sensor.enterCallback(sensor.entity, sensor.index, entity); + if (hitbox->prevGrounded && !hitbox->grounded) { + auto appliedVelocity = hitbox->groundVelocity / dt; + vel += appliedVelocity; + hitbox->groundVelocity = {}; + } + + AABB aabb; + aabb.a = pos - hitbox->getHalfSize(); + aabb.b = pos + hitbox->getHalfSize(); + for (size_t i = 0; i < sensors.size(); i++) { + auto& sensor = *sensors[i]; + if (sensor.entity == hitbox->entity) { + continue; } - sensor.nextEntered.insert(entity); + bool triggered = false; + switch (sensor.type) { + case SensorType::AABB: + triggered = aabb.intersects(sensor.calculated.aabb); + break; + case SensorType::RADIUS: + triggered = glm::distance2( + pos, glm::vec3(sensor.calculated.radial)) + < sensor.calculated.radial.w; + break; + } + if (!triggered) { + continue; + } + if (sensor.prevEntered.find(hitbox->entity) == sensor.prevEntered.end()) { + sensor.enterCallback(sensor.entity, sensor.index, hitbox->entity); + } + sensor.nextEntered.insert(hitbox->entity); } + hitbox->friction = glm::abs(hitbox->gravityScale <= 1e-7f) + ? 8.0f + : (!hitbox->prevGrounded ? 2.0f : 10.0f); } } diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 419b37ecc..ee02f052b 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -18,10 +18,8 @@ public: PhysicsSolver(glm::vec3 gravity); void step( const GlobalChunks& chunks, - Hitbox& hitbox, float delta, - uint substeps, - entityid_t entity + uint substeps ); auto& getSensorsWriteable() { @@ -32,11 +30,16 @@ public: return solidHitboxes; } + auto& getHitboxesWriteable() { + return hitboxes; + } + void removeSensor(Sensor* sensor); private: glm::vec3 gravity; std::vector sensors; std::vector solidHitboxes; + std::vector hitboxes; void calcCollisions( const GlobalChunks& chunks, @@ -52,7 +55,6 @@ private: Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, - bool prevGrounded, float dt, int substeps ); From 38b478bc8d85ae2607805c29c30d70401bc8a7cf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Mar 2026 22:13:41 +0300 Subject: [PATCH 48/73] cleanup --- src/physics/Hitbox.cpp | 2 -- src/physics/PhysicsSolver.cpp | 61 ++++++++++++++++++----------------- src/physics/PhysicsSolver.hpp | 2 ++ 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp index e9e18ce58..f49e0c2b8 100644 --- a/src/physics/Hitbox.cpp +++ b/src/physics/Hitbox.cpp @@ -1,7 +1,5 @@ #include "Hitbox.hpp" -#include - Hitbox::Hitbox( entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize ) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index cf44024f1..cc0986364 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -392,9 +392,8 @@ void PhysicsSolver::step( for (auto hitbox : hitboxes) { float linearDamping = hitbox->linearDamping * hitbox->friction; - glm::vec3& pos = hitbox->position; - glm::vec3& vel = hitbox->velocity; + glm::vec3& vel = hitbox->velocity; vel.x /= 1.0f + delta * linearDamping; vel.z /= 1.0f + delta * linearDamping; if (hitbox->verticalDamping > 0.0f) { @@ -406,39 +405,43 @@ void PhysicsSolver::step( hitbox->groundVelocity = {}; } - AABB aabb; - aabb.a = pos - hitbox->getHalfSize(); - aabb.b = pos + hitbox->getHalfSize(); - for (size_t i = 0; i < sensors.size(); i++) { - auto& sensor = *sensors[i]; - if (sensor.entity == hitbox->entity) { - continue; - } - bool triggered = false; - switch (sensor.type) { - case SensorType::AABB: - triggered = aabb.intersects(sensor.calculated.aabb); - break; - case SensorType::RADIUS: - triggered = glm::distance2( - pos, glm::vec3(sensor.calculated.radial)) - < sensor.calculated.radial.w; - break; - } - if (!triggered) { - continue; - } - if (sensor.prevEntered.find(hitbox->entity) == sensor.prevEntered.end()) { - sensor.enterCallback(sensor.entity, sensor.index, hitbox->entity); - } - sensor.nextEntered.insert(hitbox->entity); - } + updateSensors(*hitbox); hitbox->friction = glm::abs(hitbox->gravityScale <= 1e-7f) ? 8.0f : (!hitbox->prevGrounded ? 2.0f : 10.0f); } } + +void PhysicsSolver::updateSensors(Hitbox& hitbox) { + auto aabb = hitbox.getAABB(); + + for (size_t i = 0; i < sensors.size(); i++) { + auto& sensor = *sensors[i]; + if (sensor.entity == hitbox.entity) { + continue; + } + bool triggered = false; + switch (sensor.type) { + case SensorType::AABB: + triggered = aabb.intersects(sensor.calculated.aabb); + break; + case SensorType::RADIUS: + triggered = glm::distance2( + hitbox.position, glm::vec3(sensor.calculated.radial)) + < sensor.calculated.radial.w; + break; + } + if (!triggered) { + continue; + } + if (sensor.prevEntered.find(hitbox.entity) == sensor.prevEntered.end()) { + sensor.enterCallback(sensor.entity, sensor.index, hitbox.entity); + } + sensor.nextEntered.insert(hitbox.entity); + } +} + void PhysicsSolver::removeSensor(Sensor* sensor) { sensors.erase( std::remove(sensors.begin(), sensors.end(), sensor), sensors.end() diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index ee02f052b..3d167befd 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -58,4 +58,6 @@ private: float dt, int substeps ); + + void updateSensors(Hitbox& hitbox); }; From b7cd116e62fc482e9b171b9dac5632e05831ed0c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Mar 2026 23:11:23 +0300 Subject: [PATCH 49/73] cleanup --- src/physics/PhysicsSolver.cpp | 27 ++++++++------------------- src/physics/PhysicsSolver.hpp | 20 +++++++------------- src/world/Level.cpp | 2 +- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index cc0986364..9c1ed42ab 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -17,7 +17,7 @@ inline constexpr float MAX_FIX = 0.15f; static debug::Logger logger("physics-solver"); -PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(std::move(gravity)) {} +PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity) : chunks(chunks), gravity(std::move(gravity)) {} static float calc_step_height( const GlobalChunks& chunks, @@ -116,12 +116,7 @@ static void calc_collision( } } -static bool calc_collision_neg_y( - Hitbox& hitbox, - const GlobalChunks& chunks, - const std::vector& solidHitboxes, - const glm::vec3& half -) { +bool PhysicsSolver::calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half) { auto& pos = hitbox.position; auto& vel = hitbox.velocity; @@ -183,10 +178,9 @@ static bool calc_collision_neg_y( } void PhysicsSolver::calcCollisions( - const GlobalChunks& chunks, - Hitbox& hitbox, - glm::vec3& vel, - glm::vec3& pos, + Hitbox& hitbox, + glm::vec3& vel, + glm::vec3& pos, const glm::vec3& half, float stepHeight ) { @@ -205,7 +199,7 @@ void PhysicsSolver::calcCollisions( calc_collision<2, 1, 0, 1>(hitbox, chunks, solidHitboxes, half, stepHeight); pos.x = xpos; - if (calc_collision_neg_y(hitbox, chunks, solidHitboxes, half)) { + if (calcCollisionNegY(hitbox, half)) { hitbox.grounded = true; } @@ -290,7 +284,6 @@ void PhysicsSolver::calcCollisions( } void PhysicsSolver::calcSubstep( - const GlobalChunks& chunks, Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, @@ -311,7 +304,6 @@ void PhysicsSolver::calcSubstep( if (hitbox.type == BodyType::DYNAMIC) { calcCollisions( - chunks, hitbox, vel, pos, @@ -369,9 +361,7 @@ void PhysicsSolver::calcSubstep( } void PhysicsSolver::step( - const GlobalChunks& chunks, - float delta, - uint substeps + const GlobalChunks& chunks, float delta, uint substeps ) { for (auto hitbox : hitboxes) { hitbox->groundMaterial.clear(); @@ -386,7 +376,7 @@ void PhysicsSolver::step( glm::vec3& pos = hitbox->position; glm::vec3& vel = hitbox->velocity; hitbox->prevPosition = hitbox->position; - calcSubstep(chunks, *hitbox, vel, pos, dt, substeps); + calcSubstep(*hitbox, vel, pos, dt, substeps); } } @@ -412,7 +402,6 @@ void PhysicsSolver::step( } } - void PhysicsSolver::updateSensors(Hitbox& hitbox) { auto aabb = hitbox.getAABB(); diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 3d167befd..e7b5138a5 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -15,12 +15,9 @@ struct Sensor; class PhysicsSolver { public: - PhysicsSolver(glm::vec3 gravity); - void step( - const GlobalChunks& chunks, - float delta, - uint substeps - ); + PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity); + + void step(const GlobalChunks& chunks, float delta, uint substeps); auto& getSensorsWriteable() { return sensors; @@ -36,13 +33,13 @@ public: void removeSensor(Sensor* sensor); private: + const GlobalChunks& chunks; glm::vec3 gravity; std::vector sensors; std::vector solidHitboxes; std::vector hitboxes; void calcCollisions( - const GlobalChunks& chunks, Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, @@ -51,13 +48,10 @@ private: ); void calcSubstep( - const GlobalChunks& chunks, - Hitbox& hitbox, - glm::vec3& vel, - glm::vec3& pos, - float dt, - int substeps + Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt, int substeps ); + bool calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half); + void updateSensors(Hitbox& hitbox); }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 2a1583bd7..e6d9000e1 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -26,7 +26,7 @@ Level::Level( : world(std::move(worldPtr)), content(content), chunks(std::make_unique(*this)), - physics(std::make_unique(glm::vec3(0, -22.6f, 0))), + physics(std::make_unique(*chunks, glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(*this)), players(std::make_unique(*this)), From f4e55f9938d339a7ac9f00934488fce8b23ad85f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Mar 2026 23:59:03 +0300 Subject: [PATCH 50/73] update MAX_FIX value --- src/physics/PhysicsSolver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 9c1ed42ab..aa2635de8 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -13,11 +13,13 @@ #include inline constexpr float E = 0.03f; -inline constexpr float MAX_FIX = 0.15f; +inline constexpr float MAX_FIX = 0.05f; static debug::Logger logger("physics-solver"); -PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity) : chunks(chunks), gravity(std::move(gravity)) {} +PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity) + : chunks(chunks), gravity(std::move(gravity)) { +} static float calc_step_height( const GlobalChunks& chunks, From 06d03f889cc8900a825652508e7b2e029b0366a2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Mar 2026 02:53:43 +0300 Subject: [PATCH 51/73] feat: new linear damping behaviour --- src/physics/PhysicsSolver.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index aa2635de8..bd5da28ed 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -30,14 +30,15 @@ static float calc_step_height( AABB aabb(-half, +half); aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2)); aabb = aabb + pos + glm::vec3(0.0f, stepHeight, 0.0f); - if (stepHeight > 0.0f) { - for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { - float x = (pos.x - half.x) + ix; - for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { - float z = (pos.z - half.z) + iz; - if (chunks.isObstacleAt(x, pos.y + half.y + stepHeight, z, aabb)) { - return 0.0f; - } + if (stepHeight <= 0.0f) { + return stepHeight; + } + for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { + float x = (pos.x - half.x) + ix; + for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) { + float z = (pos.z - half.z) + iz; + if (chunks.isObstacleAt(x, pos.y + half.y + stepHeight, z, aabb)) { + return 0.0f; } } } @@ -293,11 +294,9 @@ void PhysicsSolver::calcSubstep( int substeps ) { if (glm::length2(hitbox.groundVelocity) > 1e-6f) { - pos.x += hitbox.groundVelocity.x; if (hitbox.groundVelocity.y < 0.0f) { pos.y += hitbox.groundVelocity.y; } - pos.z += hitbox.groundVelocity.z; } auto initpos = pos; @@ -386,8 +385,10 @@ void PhysicsSolver::step( float linearDamping = hitbox->linearDamping * hitbox->friction; glm::vec3& vel = hitbox->velocity; - vel.x /= 1.0f + delta * linearDamping; - vel.z /= 1.0f + delta * linearDamping; + auto diff = hitbox->groundVelocity / dt - vel; + vel.x += diff.x * delta * linearDamping; + vel.z += diff.z * delta * linearDamping; + if (hitbox->verticalDamping > 0.0f) { vel.y /= 1.0f + delta * linearDamping * hitbox->verticalDamping; } From 33f7815a21518281401390482f1122b809bd8f17 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 13 Mar 2026 21:19:47 +0300 Subject: [PATCH 52/73] add io_stream:seek --- .../internal/stream_providers/file.lua | 1 + .../stream_providers/named_pipe_unix.lua | 4 ++ .../stream_providers/named_pipe_windows.lua | 8 +++- res/modules/io_stream.lua | 4 ++ src/logic/scripting/lua/libs/libfile.cpp | 37 ++++++++++++++++++- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/res/modules/internal/stream_providers/file.lua b/res/modules/internal/stream_providers/file.lua index 80bc8c5bb..ec81a42c0 100644 --- a/res/modules/internal/stream_providers/file.lua +++ b/res/modules/internal/stream_providers/file.lua @@ -3,6 +3,7 @@ local io_stream = require "core:io_stream" local lib = { read = file.__read_descriptor, write = file.__write_descriptor, + seek = file.__seek_descriptor, flush = file.__flush_descriptor, is_alive = file.__has_descriptor, close = file.__close_descriptor diff --git a/res/modules/internal/stream_providers/named_pipe_unix.lua b/res/modules/internal/stream_providers/named_pipe_unix.lua index 1843dabaa..8b924f47a 100644 --- a/res/modules/internal/stream_providers/named_pipe_unix.lua +++ b/res/modules/internal/stream_providers/named_pipe_unix.lua @@ -58,6 +58,10 @@ function lib.write(fd, bytearray) end end +function lib.seek(fd, mode, offset) + error("cannot seek the named pipe") +end + function lib.flush(fd) -- no flush on unix end diff --git a/res/modules/internal/stream_providers/named_pipe_windows.lua b/res/modules/internal/stream_providers/named_pipe_windows.lua index 83691aa5a..3d4b31af1 100644 --- a/res/modules/internal/stream_providers/named_pipe_windows.lua +++ b/res/modules/internal/stream_providers/named_pipe_windows.lua @@ -62,7 +62,7 @@ function lib.read(handle, len) if not has_data then return out - elseif hasData == -1 then + elseif has_data == -1 then error("failed to read from named pipe: "..tostring(C.GetLastError())) end @@ -84,8 +84,8 @@ end function lib.write(handle, bytearray) local len = #bytearray - local buffer = FFI.new("uint8_t[?]", len) + for i = 1, len do buffer[i-1] = bytearray[i] end @@ -97,6 +97,10 @@ function lib.write(handle, bytearray) end end +function lib.seek(handle, mode, offset) + error("cannot seek the named pipe") +end + function lib.flush(handle) C.FlushFileBuffers(handle) end diff --git a/res/modules/io_stream.lua b/res/modules/io_stream.lua index 15ba85d12..b608a22a9 100644 --- a/res/modules/io_stream.lua +++ b/res/modules/io_stream.lua @@ -369,6 +369,10 @@ function io_stream:write(arg, ...) end end +function io_stream:seek(mode, offset) + self.ioLib.seek(self.descriptor, mode, offset) +end + function io_stream:is_alive() return self.ioLib.is_alive(self.descriptor) end diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index 687008152..26248379c 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -347,6 +347,41 @@ static int l_write_descriptor(lua::State* L) { return 0; } +static int l_seek_descriptor(lua::State* L) { + int descriptor = lua::tointeger(L, 1); + + if (!scripting::descriptors_manager::has_descriptor(descriptor)) { + throw std::runtime_error("unknown descriptor"); + } + + std::string mode = lua::require_string(L, 2); + std::ios_base::seekdir dir; + + switch (mode[0]) { + case 'b': + dir = std::ios_base::beg; + break; + case 'c': + dir = std::ios_base::cur; + break; + case 'e': + dir = std::ios_base::end; + break; + default: + throw std::runtime_error("invalid seek mode"); + } + + auto* stream = scripting::descriptors_manager::get_output(descriptor); + + stream->seekp(lua::tointeger(L, 3), dir); + + if (!stream->good()) { + throw std::runtime_error("failed to seek stream"); + } + + return 0; +} + static int l_flush_descriptor(lua::State* L) { int descriptor = lua::tointeger(L, 1); @@ -406,9 +441,9 @@ const luaL_Reg filelib[] = { {"__has_descriptor", lua::wrap}, {"__read_descriptor", lua::wrap}, {"__write_descriptor", lua::wrap}, + {"__seek_descriptor", lua::wrap}, {"__flush_descriptor", lua::wrap}, {"__close_descriptor", lua::wrap}, {"__close_all_descriptors", lua::wrap}, {nullptr, nullptr} }; - From 81858bc7f83790a8c2acca0a52e9a665f386b9cb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Mar 2026 21:31:20 +0300 Subject: [PATCH 53/73] feat: applying Y velocity --- src/physics/Hitbox.hpp | 1 - src/physics/PhysicsSolver.cpp | 42 ++++++++++++----------------------- src/physics/PhysicsSolver.hpp | 9 ++++---- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 9d2213572..476e1a4aa 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -61,7 +61,6 @@ struct Hitbox { float friction = 1.0f; float verticalDamping = 1.0f; bool grounded = false; - bool yCollided = false; float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index bd5da28ed..939caf303 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -119,7 +119,9 @@ static void calc_collision( } } -bool PhysicsSolver::calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half) { +bool PhysicsSolver::calcCollisionNegY( + Hitbox& hitbox, const glm::vec3& half, float dt +) { auto& pos = hitbox.position; auto& vel = hitbox.velocity; @@ -140,8 +142,8 @@ bool PhysicsSolver::calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half) { pos.y = newy; } hitbox.groundVelocity = box->position - box->prevPosition; - if (vel.y < 0.0f) { - vel.y = 0.0f; + if (vel.y < hitbox.groundVelocity.y / dt) { + vel.y = hitbox.groundVelocity.y / dt * (1.0f - E); if (hitbox.groundMaterial.empty() && !box->material.empty()) { hitbox.groundMaterial = box->material; } @@ -185,9 +187,9 @@ void PhysicsSolver::calcCollisions( glm::vec3& vel, glm::vec3& pos, const glm::vec3& half, - float stepHeight + float stepHeight, + float dt ) { - hitbox.yCollided = false; stepHeight = calc_step_height(chunks, pos, half, stepHeight); auto prevPos = pos; @@ -202,7 +204,7 @@ void PhysicsSolver::calcCollisions( calc_collision<2, 1, 0, 1>(hitbox, chunks, solidHitboxes, half, stepHeight); pos.x = xpos; - if (calcCollisionNegY(hitbox, half)) { + if (calcCollisionNegY(hitbox, half, dt)) { hitbox.grounded = true; } @@ -221,7 +223,6 @@ void PhysicsSolver::calcCollisions( if (pos.y >= newy) { vel.y = 0.0f; pos.y = newy; - hitbox.yCollided = true; } break; } @@ -245,10 +246,6 @@ void PhysicsSolver::calcCollisions( } } - if (hitbox.yCollided) { - pos.y = prevPos.y; - } - // step on if (stepHeight > 0.0 && vel.y <= 0.0f) { AABB boxAABB = AABB(pos - half, pos + half); @@ -287,18 +284,8 @@ void PhysicsSolver::calcCollisions( } void PhysicsSolver::calcSubstep( - Hitbox& hitbox, - glm::vec3& vel, - glm::vec3& pos, - float dt, - int substeps + Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt ) { - if (glm::length2(hitbox.groundVelocity) > 1e-6f) { - if (hitbox.groundVelocity.y < 0.0f) { - pos.y += hitbox.groundVelocity.y; - } - } - auto initpos = pos; auto half = hitbox.getHalfSize(); float gravityScale = hitbox.gravityScale; @@ -309,7 +296,9 @@ void PhysicsSolver::calcSubstep( vel, pos, half, - (hitbox.prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f + (hitbox.prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight + : 0.0f, + dt ); } @@ -375,9 +364,8 @@ void PhysicsSolver::step( for (uint i = 0; i < substeps; i++) { for (auto hitbox : hitboxes) { glm::vec3& pos = hitbox->position; - glm::vec3& vel = hitbox->velocity; hitbox->prevPosition = hitbox->position; - calcSubstep(*hitbox, vel, pos, dt, substeps); + calcSubstep(*hitbox, hitbox->velocity, pos, dt); } } @@ -392,9 +380,7 @@ void PhysicsSolver::step( if (hitbox->verticalDamping > 0.0f) { vel.y /= 1.0f + delta * linearDamping * hitbox->verticalDamping; } - if (hitbox->prevGrounded && !hitbox->grounded) { - auto appliedVelocity = hitbox->groundVelocity / dt; - vel += appliedVelocity; + if (!hitbox->grounded) { hitbox->groundVelocity = {}; } diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index e7b5138a5..e616696ca 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -44,14 +44,13 @@ private: glm::vec3& vel, glm::vec3& pos, const glm::vec3& half, - float stepHeight + float stepHeight, + float dt ); - void calcSubstep( - Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt, int substeps - ); + void calcSubstep(Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt); - bool calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half); + bool calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half, float dt); void updateSensors(Hitbox& hitbox); }; From ed57d2c84d8005a64b0e3ec6403f4eca7253aabc Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 13 Mar 2026 21:42:20 +0300 Subject: [PATCH 54/73] add docs --- doc/ru/scripting/io_stream.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/doc/ru/scripting/io_stream.md b/doc/ru/scripting/io_stream.md index 283e0c48b..e8e84063f 100644 --- a/doc/ru/scripting/io_stream.md +++ b/doc/ru/scripting/io_stream.md @@ -3,15 +3,16 @@ Класс, предназначенный для работы с потоками ## Содержание -- [Режимы](#режимы) - - [general](#general) - - [flush](#flush) - - [binary](#binary) -- Методы - - [режимы и их состояния](#методы-режимов-и-их-состояний) - - [I/O](#io-методы) - - [Buffered-методы](#методы-buffered-режима) - - [Методы потока](#методы-контроля-состояния-потока) +- [Класс *io\_stream*](#класс-io_stream) + - [Содержание](#содержание) + - [Режимы](#режимы) + - [general](#general) + - [flush](#flush) + - [binary](#binary) + - [Методы режимов и их состояний](#методы-режимов-и-их-состояний) + - [I/O методы](#io-методы) + - [Методы Buffered-режима](#методы-buffered-режима) + - [Методы контроля состояния потока](#методы-контроля-состояния-потока) ## Режимы @@ -137,6 +138,18 @@ io_stream:write_line(string) io_stream:read_fully( [опционально] useTable: boolean ) -> Bytearray | table | table | string + +--[[ +Устанавливает позицию в потоке +Режимы: + b - Задаёт позицию относительно начало файла + c - Задаёт позицию относительно текущей позиции + e - Задаёт позицию относительно конца файла +--]] +io_stream:seek( + mode: string + offset: number +) ``` ## Методы Buffered-режима From 98431f256cea28320c3d90670367a36567e88e8c Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 13 Mar 2026 21:47:24 +0300 Subject: [PATCH 55/73] fix --- doc/ru/scripting/io_stream.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/ru/scripting/io_stream.md b/doc/ru/scripting/io_stream.md index e8e84063f..cc3403eda 100644 --- a/doc/ru/scripting/io_stream.md +++ b/doc/ru/scripting/io_stream.md @@ -3,16 +3,15 @@ Класс, предназначенный для работы с потоками ## Содержание -- [Класс *io\_stream*](#класс-io_stream) - - [Содержание](#содержание) - - [Режимы](#режимы) - - [general](#general) - - [flush](#flush) - - [binary](#binary) - - [Методы режимов и их состояний](#методы-режимов-и-их-состояний) - - [I/O методы](#io-методы) - - [Методы Buffered-режима](#методы-buffered-режима) - - [Методы контроля состояния потока](#методы-контроля-состояния-потока) +- [Режимы](#режимы) + - [general](#general) + - [flush](#flush) + - [binary](#binary) +- Методы + - [режимы и их состояния](#методы-режимов-и-их-состояний) + - [I/O](#io-методы) + - [Buffered-методы](#методы-buffered-режима) + - [Методы потока](#методы-контроля-состояния-потока) ## Режимы From 15bbf55e2939b8be80838c9902a96fe269dd4666 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 14:28:23 +0300 Subject: [PATCH 56/73] improve --- src/physics/PhysicsSolver.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 939caf303..96a4dfbe0 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -305,10 +305,6 @@ void PhysicsSolver::calcSubstep( vel += gravity * dt * gravityScale; pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f; - if (hitbox.grounded && pos.y < initpos.y) { - pos.y = initpos.y; - } - // crouching if (!hitbox.crouching || !hitbox.grounded) { return; @@ -322,7 +318,7 @@ void PhysicsSolver::calcSubstep( checkPos.z = axis == 0 ? initpos.z : pos.z; AABB boxAABB(checkPos - half, checkPos + half); - boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f, 1.0f - E * 4)); + boxAABB.scale(glm::vec3(1.0f - E * 4, 1.5f, 1.0f - E * 4)); for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) { float x = (pos.x - half.x + E) + ix; for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){ @@ -337,7 +333,7 @@ void PhysicsSolver::calcSubstep( if (glm::distance2(box->position, pos) < E) { continue; } - if (box->getAABB().intersects(boxAABB)) { + if (box->position.y < pos.y && box->getAABB().intersects(boxAABB)) { hitbox.grounded = true; break; } From fc57e98a96f715b82cdb7f2351ba0e7f43423f3e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 15:47:26 +0300 Subject: [PATCH 57/73] feat: third-person camera now checks solid entities --- src/logic/PlayerController.cpp | 26 ++++++++++++++++++++------ src/objects/Entities.cpp | 8 ++++++-- src/objects/Entities.hpp | 4 +++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 3d8c75872..663ff1fa1 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -44,7 +44,9 @@ namespace { const float CROUCH_SHIFT_Y = -0.2f; } -CameraControl::CameraControl(Player& player, const CameraSettings& settings) +CameraControl::CameraControl( + Player& player, const CameraSettings& settings +) : player(player), camera(player.fpCamera), settings(settings), @@ -195,18 +197,30 @@ void CameraControl::update( refreshPosition(); + auto castRay = [](Player& player, Camera& camera, const glm::vec3& front) + -> glm::vec3 { + auto blockEnd = player.chunks->rayCastToObstacle(camera.position, front, 3.0f); + auto entityRay = player.getLevel().entities->rayCast( + camera.position, front, 3.0f, player.getEntity(), true + ); + if (entityRay.has_value() && + entityRay->distance < glm::distance(camera.position, blockEnd)) { + return (camera.position + front * entityRay->distance); + } else { + return blockEnd; + } + }; + camera->updateVectors(); if (player.currentCamera == spCamera) { spCamera->position = - chunks.rayCastToObstacle(camera->position, camera->front, 3.0f) - - 0.4f * camera->front; + castRay(player, *camera, camera->front) - 0.4f * camera->front; spCamera->dir = -camera->dir; spCamera->front = -camera->front; spCamera->right = -camera->right; } else if (player.currentCamera == tpCamera) { - tpCamera->position = - chunks.rayCastToObstacle(camera->position, -camera->front, 3.0f) + - 0.4f * camera->front; + tpCamera->position = castRay(player, *camera, camera->front * -1.0f) + + 0.4f * camera->front; tpCamera->dir = camera->dir; tpCamera->front = camera->front; tpCamera->right = camera->right; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 259e9e3c6..e445b738a 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -162,7 +162,11 @@ void Entities::loadEntity(const dv::value& map, Entity entity) { } std::optional Entities::rayCast( - glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore + glm::vec3 start, + glm::vec3 dir, + float maxDistance, + entityid_t ignore, + bool solidOnly ) { Ray ray(start, dir); auto view = registry->view(); @@ -171,7 +175,7 @@ std::optional Entities::rayCast( glm::ivec3 foundNormal; for (auto [entity, eid, transform, body] : view.each()) { - if (eid.uid == ignore || !body.enabled) { + if (eid.uid == ignore || !body.enabled || (solidOnly && !eid.def.solid)) { continue; } auto& hitbox = body.hitbox; diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 804ce17f4..33786118a 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -88,12 +88,14 @@ public: /// @param dir Ray direction normalized vector /// @param maxDistance Max ray length /// @param ignore Ignored entity ID + /// @param solidOnly If true, only entities with solid hitboxes will be checked /// @return An optional structure containing entity, normal and distance std::optional rayCast( glm::vec3 start, glm::vec3 dir, float maxDistance, - entityid_t ignore = -1 + entityid_t ignore = -1, + bool solidOnly = false ); void loadEntities(dv::value map); From 3bcb7f7e702695299d0606a3e73a6f26d0fbcb4b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 17:46:23 +0300 Subject: [PATCH 58/73] improve floor collision physics (add mass) --- src/physics/Hitbox.hpp | 2 ++ src/physics/PhysicsSolver.cpp | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 476e1a4aa..ecf41faa3 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -64,6 +64,8 @@ struct Hitbox { float gravityScale = 1.0f; bool crouching = false; float stepHeight = 0.5f; + float mass = 1.0f; + float elasticity = 0.0f; std::string material; std::string groundMaterial; glm::vec3 groundVelocity {}; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 96a4dfbe0..dfea20b67 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -141,9 +141,18 @@ bool PhysicsSolver::calcCollisionNegY( if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) { pos.y = newy; } + const auto& vA = hitbox.velocity; + const auto& vB = box->velocity; + const auto& mA = hitbox.mass; + const auto& mB = box->mass; + + auto velA = (mA * vA + mB * vB + hitbox.elasticity * mB * (vB - vA)) / (mA + mB); + auto velB = (mA * vA + mB * vB + box->elasticity * mA * (vA - vB)) / (mA + mB); + hitbox.groundVelocity = box->position - box->prevPosition; if (vel.y < hitbox.groundVelocity.y / dt) { - vel.y = hitbox.groundVelocity.y / dt * (1.0f - E); + vel.y = velA.y; + box->velocity.y = velB.y; if (hitbox.groundMaterial.empty() && !box->material.empty()) { hitbox.groundMaterial = box->material; } @@ -171,8 +180,9 @@ bool PhysicsSolver::calcCollisionNegY( if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) { float newy = std::floor(coord.y) + half.y + obstacle->max().y; + if (newy >= pos.y) { - vel.y = 0.0f; + vel.y = -hitbox.elasticity * vel.y; pos.y = newy; } return true; From f945719d7ab1237a2d903f54fd26f5ad3510da62 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 18:22:18 +0300 Subject: [PATCH 59/73] improve all directions of collision --- src/physics/PhysicsSolver.cpp | 40 ++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index dfea20b67..c680d3f5b 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -21,6 +21,16 @@ PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity) : chunks(chunks), gravity(std::move(gravity)) { } +static glm::vec3 calc_collsion_velocity_result( + const Hitbox& a, const Hitbox& b +) { + const auto& vA = a.velocity; + const auto& vB = b.velocity; + const auto& mA = a.mass; + const auto& mB = b.mass; + return (mA * vA + mB * vB + a.elasticity * mB * (vB - vA)) / (mA + mB); +} + static float calc_step_height( const GlobalChunks& chunks, const glm::vec3& pos, @@ -45,6 +55,8 @@ static float calc_step_height( return stepHeight; } +// todo: reduce code duplication + template static void calc_collision( Hitbox& hitbox, @@ -83,8 +95,13 @@ static void calc_collision( continue; } if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) { - if (vel[nx] * sign > 0.0f) { - vel[nx] = 0.0f; + auto velA = calc_collsion_velocity_result(hitbox, *box); + auto velB = calc_collsion_velocity_result(*box, hitbox); + if ((vel[nx] - box->velocity[nx]) * sign > 0.0f) { + vel[nx] = velA[nx]; + vel[nz] = velA[nz]; + box->velocity[nx] = velB[nx]; + box->velocity[nz] = velB[nz]; } pos[nx] = newx; } @@ -110,7 +127,7 @@ static void calc_collision( float newx = std::floor(coord[nx]) - half[nx] * sign + (sign > 0 ? obstacle->min() : obstacle->max())[nx]; if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) { - vel[nx] = 0.0f; + vel[nx] = -hitbox.elasticity * vel[nx]; pos[nx] = newx; } return; @@ -141,13 +158,9 @@ bool PhysicsSolver::calcCollisionNegY( if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) { pos.y = newy; } - const auto& vA = hitbox.velocity; - const auto& vB = box->velocity; - const auto& mA = hitbox.mass; - const auto& mB = box->mass; - auto velA = (mA * vA + mB * vB + hitbox.elasticity * mB * (vB - vA)) / (mA + mB); - auto velB = (mA * vA + mB * vB + box->elasticity * mA * (vA - vB)) / (mA + mB); + auto velA = calc_collsion_velocity_result(hitbox, *box); + auto velB = calc_collsion_velocity_result(*box, hitbox); hitbox.groundVelocity = box->position - box->prevPosition; if (vel.y < hitbox.groundVelocity.y / dt) { @@ -231,7 +244,7 @@ void PhysicsSolver::calcCollisions( if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) { float newy = std::floor(y) - half.y + aabb->min().y - E; if (pos.y >= newy) { - vel.y = 0.0f; + vel.y = -hitbox.elasticity * vel.y; pos.y = newy; } break; @@ -248,8 +261,11 @@ void PhysicsSolver::calcCollisions( if (pos.y > newy && glm::abs(pos.y - newy) < 0.5f) { pos.y = newy; } - if (vel.y > 0.0f) { - vel.y = 0.0f; + auto velA = calc_collsion_velocity_result(hitbox, *box); + auto velB = calc_collsion_velocity_result(*box, hitbox); + if (vel.y > hitbox.groundVelocity.y / dt) { + vel.y = velA.y; + box->velocity.y = velB.y; break; } } From ad32d7b6db3c307d9b2a25c73ab765321ea7eb8a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 18:47:10 +0300 Subject: [PATCH 60/73] add 'mass' and 'elasticity' entity properties --- src/content/loading/EntityLoader.cpp | 2 ++ src/objects/Entities.cpp | 4 ++++ src/objects/EntityDef.cpp | 2 ++ src/objects/EntityDef.hpp | 6 ++++++ src/physics/PhysicsSolver.cpp | 6 ++++++ 5 files changed, 20 insertions(+) diff --git a/src/content/loading/EntityLoader.cpp b/src/content/loading/EntityLoader.cpp index b8dfa9f4e..91967ea89 100644 --- a/src/content/loading/EntityLoader.cpp +++ b/src/content/loading/EntityLoader.cpp @@ -89,5 +89,7 @@ template<> void ContentUnitLoader::loadUnit( root.at("skeleton-name").get(def.skeletonName); root.at("blocking").get(def.blocking); root.at("solid").get(def.solid); + root.at("mass").get(def.mass); + root.at("elasticity").get(def.elasticity); root.at("step-height").get(def.stepHeight); } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index e445b738a..bc9c0860d 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -22,6 +22,7 @@ #include #include +#include #include static debug::Logger logger("entities"); @@ -318,6 +319,9 @@ void Entities::preparePhysics(float delta) { if (!eid.def.solid) { continue; } + rigidbody.hitbox.mass = eid.def.bodyType == BodyType::DYNAMIC + ? eid.def.mass + : std::numeric_limits::infinity(); solidHitboxes.emplace_back(&rigidbody.hitbox); } } diff --git a/src/objects/EntityDef.cpp b/src/objects/EntityDef.cpp index cab635584..ae1f87c68 100644 --- a/src/objects/EntityDef.cpp +++ b/src/objects/EntityDef.cpp @@ -10,6 +10,8 @@ void EntityDef::cloneTo(EntityDef& dst) { dst.skeletonName = skeletonName; dst.blocking = blocking; dst.solid = solid; + dst.mass = mass; + dst.elasticity = elasticity; dst.stepHeight = stepHeight; dst.save = save; } diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index ad93d445e..195d07a17 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -45,6 +45,12 @@ struct EntityDef { /// @brief Is the entity solid (blocks other entities) bool solid = false; + /// @brief Mass for physics calculations + float mass = 1.0f; + + /// @brief Elasticity for physics calculations + float elasticity = 0.0f; + /// @brief Max obstacle height that does not require jumping float stepHeight = 0.5f; diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index c680d3f5b..65a2191d2 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -26,6 +26,12 @@ static glm::vec3 calc_collsion_velocity_result( ) { const auto& vA = a.velocity; const auto& vB = b.velocity; + if (glm::isinf(a.mass)) { + return vA; + } + if (glm::isinf(b.mass)) { + return vA * -a.elasticity + vB; + } const auto& mA = a.mass; const auto& mB = b.mass; return (mA * vA + mB * vB + a.elasticity * mB * (vB - vA)) / (mA + mB); From 3ea3836f844f4a9694141d87f7905bca7435fe43 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 18:49:59 +0300 Subject: [PATCH 61/73] fix calc_collsion_velocity_result for kinematic other body --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 65a2191d2..a93e4fbec 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -30,7 +30,7 @@ static glm::vec3 calc_collsion_velocity_result( return vA; } if (glm::isinf(b.mass)) { - return vA * -a.elasticity + vB; + return vB - a.elasticity * (vA - vB); } const auto& mA = a.mass; const auto& mB = b.mass; From 436601a5b0450c3437494f69348907d903d1af5f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 18:53:32 +0300 Subject: [PATCH 62/73] fix Entities::preparePhysics --- src/objects/Entities.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index bc9c0860d..5d55098c2 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -315,13 +315,14 @@ void Entities::preparePhysics(float delta) { if (eid.destroyFlag || !rigidbody.enabled) { continue; } + rigidbody.hitbox.mass = eid.def.bodyType == BodyType::DYNAMIC + ? eid.def.mass + : std::numeric_limits::infinity(); + rigidbody.hitbox.elasticity = eid.def.elasticity; hitboxes.emplace_back(&rigidbody.hitbox); if (!eid.def.solid) { continue; } - rigidbody.hitbox.mass = eid.def.bodyType == BodyType::DYNAMIC - ? eid.def.mass - : std::numeric_limits::infinity(); solidHitboxes.emplace_back(&rigidbody.hitbox); } } From 1bba713502525aa67b9b4575dab29defbacc1504 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 20:53:28 +0300 Subject: [PATCH 63/73] fix --- src/physics/PhysicsSolver.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index a93e4fbec..ed220d4cf 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -105,9 +105,7 @@ static void calc_collision( auto velB = calc_collsion_velocity_result(*box, hitbox); if ((vel[nx] - box->velocity[nx]) * sign > 0.0f) { vel[nx] = velA[nx]; - vel[nz] = velA[nz]; box->velocity[nx] = velB[nx]; - box->velocity[nz] = velB[nz]; } pos[nx] = newx; } From bd7f58880063e9cfeb9be0065a94954ec24110e1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 21:02:02 +0300 Subject: [PATCH 64/73] add mass, elasticity getters, setters to entity.rigidbody --- .../scripting/lua/libs/lib__rigidbody.cpp | 32 +++++++++++++++++++ src/objects/Entities.cpp | 4 +-- src/objects/Rigidbody.cpp | 6 ++++ src/objects/Rigidbody.hpp | 2 ++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index 1e04e3b3c..52234f91e 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -164,6 +164,34 @@ static int l_get_material(lua::State* L) { return 0; } +static int l_get_mass(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + return lua::pushnumber(L, entity->getRigidbody().mass); + } + return 0; +} + +static int l_set_mass(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + entity->getRigidbody().mass = lua::tonumber(L, 2); + } + return 0; +} + +static int l_get_elasticity(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + return lua::pushnumber(L, entity->getRigidbody().elasticity); + } + return 0; +} + +static int l_set_elasticity(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + entity->getRigidbody().elasticity = lua::tonumber(L, 2); + } + return 0; +} + const luaL_Reg rigidbodylib[] = { {"is_enabled", lua::wrap}, {"set_enabled", lua::wrap}, @@ -185,5 +213,9 @@ const luaL_Reg rigidbodylib[] = { {"set_body_type", lua::wrap}, {"set_material", lua::wrap}, {"get_material", lua::wrap}, + {"get_mass", lua::wrap}, + {"set_mass", lua::wrap}, + {"get_elasticity", lua::wrap}, + {"set_elasticity", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 5d55098c2..82e293522 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -316,9 +316,9 @@ void Entities::preparePhysics(float delta) { continue; } rigidbody.hitbox.mass = eid.def.bodyType == BodyType::DYNAMIC - ? eid.def.mass + ? rigidbody.mass : std::numeric_limits::infinity(); - rigidbody.hitbox.elasticity = eid.def.elasticity; + rigidbody.hitbox.elasticity = rigidbody.elasticity; hitboxes.emplace_back(&rigidbody.hitbox); if (!eid.def.solid) { continue; diff --git a/src/objects/Rigidbody.cpp b/src/objects/Rigidbody.cpp index 9e2edba4a..2c885f8e0 100644 --- a/src/objects/Rigidbody.cpp +++ b/src/objects/Rigidbody.cpp @@ -22,6 +22,8 @@ dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const { if (hitbox.crouching) { bodymap["crouch"] = hitbox.crouching; } + bodymap["mass"] = mass; + bodymap["elasticity"] = elasticity; } return bodymap; } @@ -33,6 +35,8 @@ void Rigidbody::deserialize(const dv::value& root) { BodyTypeMeta.getItem(bodyTypeName, hitbox.type); root["crouch"].asBoolean(hitbox.crouching); root["damping"].asNumber(hitbox.linearDamping); + root["mass"].asNumber(mass); + root["elasticity"].asNumber(elasticity); } template @@ -49,6 +53,8 @@ static sensorcallback create_sensor_callback(Entities& entities) { void Rigidbody::initialize( const EntityDef& def, entityid_t id, Entities& entities ) { + mass = def.mass; + elasticity = def.elasticity; sensors.resize(def.radialSensors.size() + def.boxSensors.size()); for (auto& [i, box] : def.boxSensors) { SensorParams params {}; diff --git a/src/objects/Rigidbody.hpp b/src/objects/Rigidbody.hpp index 91790d8a2..bc2a85f90 100644 --- a/src/objects/Rigidbody.hpp +++ b/src/objects/Rigidbody.hpp @@ -13,6 +13,8 @@ struct Rigidbody { bool enabled = true; Hitbox hitbox; std::vector sensors; + float mass; + float elasticity; dv::value serialize(bool saveVelocity, bool saveBodySettings) const; void deserialize(const dv::value& root); From 0c2e6559289c90b1bba599766dbb3629819291a4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 21:44:52 +0300 Subject: [PATCH 65/73] increase MAX_FIX value --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index ed220d4cf..92780fe5e 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -13,7 +13,7 @@ #include inline constexpr float E = 0.03f; -inline constexpr float MAX_FIX = 0.05f; +inline constexpr float MAX_FIX = 0.1f; static debug::Logger logger("physics-solver"); From c13a7110e8ed7fd503efedcfc1f01ad6c20982b9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Mar 2026 22:35:06 +0300 Subject: [PATCH 66/73] fix: flight does not affect to the first tick --- res/scripts/components/player.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/res/scripts/components/player.lua b/res/scripts/components/player.lua index 66fdda825..840bce743 100644 --- a/res/scripts/components/player.lua +++ b/res/scripts/components/player.lua @@ -60,6 +60,7 @@ end local prepared = false body:set_enabled(false) +mob:set_flight(true) function on_physics_update(delta) if not prepared then From 83f49f8cc4c16286a9dbf49aa32063c6b51af5fe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Mar 2026 12:56:51 +0300 Subject: [PATCH 67/73] update vcm bone 'move' semantics --- src/coders/vcm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coders/vcm.cpp b/src/coders/vcm.cpp index 0f408131d..738d49c09 100644 --- a/src/coders/vcm.cpp +++ b/src/coders/vcm.cpp @@ -344,9 +344,10 @@ static void perform_box(const xmlelement& root, ModelBuilder& builder) { static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context& ctx) { std::string name = root.attr("name", "").getText(); + glm::vec3 movement {}; glm::mat4 tsf(1.0f); if (root.has("move")) { - tsf = glm::translate(tsf, root.attr("move").asVec3()); + movement = root.attr("move").asVec3(); } if (root.has("rotate")) { auto text = root.attr("rotate").getText(); @@ -371,7 +372,7 @@ static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context& } builder.pop(); } else { - glm::vec3 origin = builder.getTransform() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + glm::vec3 origin = builder.getTransform() * glm::vec4(movement, 1.0f); size_t boneIndex = ctx.boneIndex++; std::vector> bones; From 35d2ea4c862beb574070ae89bde42aa8550aa468 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Mar 2026 14:30:40 +0300 Subject: [PATCH 68/73] make base:falling_block solid entity --- res/content/base/entities/falling_block.json | 3 ++- res/content/base/scripts/components/falling_block.lua | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/res/content/base/entities/falling_block.json b/res/content/base/entities/falling_block.json index cf5146fd9..b466c2a11 100644 --- a/res/content/base/entities/falling_block.json +++ b/res/content/base/entities/falling_block.json @@ -3,5 +3,6 @@ "base:falling_block" ], "skeleton-name": "base:block", - "hitbox": [0.98, 0.98, 0.98] + "hitbox": [0.98, 0.98, 0.98], + "solid": true } diff --git a/res/content/base/scripts/components/falling_block.lua b/res/content/base/scripts/components/falling_block.lua index 483e86aee..b6bbe4656 100644 --- a/res/content/base/scripts/components/falling_block.lua +++ b/res/content/base/scripts/components/falling_block.lua @@ -29,6 +29,7 @@ do -- setup visuals 0, 0, 0, 1 } rig:set_matrix(0, matrix) + body:set_material(block.material(id)) end function on_grounded() From 9297873004654b26a1391446c0005ed7776ed3d1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Mar 2026 23:42:39 +0300 Subject: [PATCH 69/73] update doc/*/entity-properties.md --- doc/en/entity-properties.md | 17 +++++++++++++++++ doc/ru/entity-properties.md | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/doc/en/entity-properties.md b/doc/en/entity-properties.md index a6530191f..bb12dcd86 100644 --- a/doc/en/entity-properties.md +++ b/doc/en/entity-properties.md @@ -95,6 +95,23 @@ Example: 0. A rectangular area with a width, height and length of 0.4 m, centered at 0.0. 1. Radial area with a radius of 1.6 m. +### *solid* + +Makes the entity's hitbox an obstacle. When enabling this property for `dynamic` body, a mass must be specified. + +### *mass* + +Defines the entity's mass. Kilograms are used as the default. +For `kinematic` bodies, an infinite mass is used instead. + +### *elasticity* + +Defines the entity's elasticity (affects the bounce on collision). + +### *step-height* + +Defines the maximum height of an obstacle an entity can step onto without jumping. + ## View ### *skeleton-name* diff --git a/doc/ru/entity-properties.md b/doc/ru/entity-properties.md index 3489e3aae..522b278a2 100644 --- a/doc/ru/entity-properties.md +++ b/doc/ru/entity-properties.md @@ -95,6 +95,23 @@ 0. Прямоугольная область шириной, высотой и длиной в 0.4 м. с центром в 0.0. 1. Радиальная область с радиусом 1.6 м. +### Осязаемость - *solid* + +Делает хитбокс сущности осязаемым препятствием. Вместе с включением данного свойства у `dynamic` тел следует указать массу (mass). + +### Масса - *mass* + +Определяет массу сущности. За стандарт приняты килограммы. +В случае `kinematic` тела вместо указанной используется бесконечная масса. + +### Эластичность - *elasticity* + +Определяет эластичность сущности (влияет на отскок при столкновении). + +### Высота шага - *step-height* + +Определяет максимальную высоту препятствия, на которое сущность может шагнуть без прыжка. + ## Вид ### Имя скелета - *skeleton-name* From f1cc11407980e9689539d72b1950e5e955a9c9c4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Mar 2026 23:48:29 +0300 Subject: [PATCH 70/73] update doc/*/xml-ui-layouts.md --- doc/en/xml-ui-layouts.md | 3 +++ doc/ru/xml-ui-layouts.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index aeb71dd62..ee4447491 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -98,6 +98,7 @@ A panel that controls the size and position of two elements. Allows the user to Inner text is a button text. +- `font` - font name - `text-align` - inner text alignment (*left/center/right*). Type: string. ## *checkbox* @@ -108,6 +109,7 @@ Inner text is a button text. ## *label* +- `font` - font name - `text-align` - text alignment (*left/center/right*). Type: string. - `valign` - vertical text alignment: top/center/bottom. - `supplier` - text supplier (called every frame). @@ -131,6 +133,7 @@ Inner text is a button text. Inner text - initially entered text +- `font` - font name - `placeholder` - placeholder text (used if the text field is empty) - `hint` - text displayed if the text field is empty (not sent to consumer, sub-consumer and validator). - `supplier` - text supplier (called every frame) diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 2e93e6f0a..c34b32844 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -100,6 +100,7 @@ Внутренний текст - текст кнопки. +- `font` - имя шрифта - `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка. ## Флажок - *checkbox* @@ -110,6 +111,7 @@ ## Метка - *label* +- `font` - имя шрифта - `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка. - `valign` - вертикальное выравнивание текста: top/center/bottom - `supplier` - поставщик текста (вызывается каждый кадр) @@ -132,6 +134,7 @@ Внутренний текст - изначально введенный текст +- `font` - имя шрифта - `placeholder` - текст подстановки (используется если текстовое поле пусто) - `hint` - текст, отображаемый, если текстовое поле пусто (не отправляется в consumer, sub-consumer и validator). - `supplier` - поставщик текста (вызывается каждый кадр) From 702a8b05f071eca83ce393917fdafa2b12191afc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Mar 2026 23:53:14 +0300 Subject: [PATCH 71/73] add bindbox to doc/*/xml-ui-layouts.md --- doc/en/xml-ui-layouts.md | 8 ++++++++ doc/ru/xml-ui-layouts.md | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index ee4447491..0bd1b5353 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -193,6 +193,14 @@ Example of list description: - `selected` - initially selected value. Default: "". - `onselect` - function to which the user-selected value is passed +## *bindbox* + +An element for displaying and editing key or mouse button bindings. + +- `font` - font name. Type: string. +- `binding` - binding name. Type: string. +- `padding` - padding between text and element borders. Type: number. Default: 6 + # Inventory elements ## *inventory* diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index c34b32844..2ca6da1ce 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -194,6 +194,14 @@ - `selected` - изначально выбранное значение. По-умолчанию: "". - `onselect` - функция, в которую передаётся выбранное пользователем значение +## Привязка ввода - *bindbox* + +Элемент для отображения и изменения привязки клавиш или кнопок мыши. + +- `font` - имя шрифта. Тип: строка. +- `binding` - имя привязки. Тип: строка. +- `padding` - отступ между текстом и границами элемента. Тип: число. По-умолчанию: 6 + # Элементы инвентаря ## Инвентарь - *inventory* From 578142c081049beacc0b7459dbb762a39676d0a1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Mar 2026 00:01:53 +0300 Subject: [PATCH 72/73] update doc/*/scripting/builtins/libgfx-weather.md --- doc/en/scripting/builtins/libgfx-weather.md | 19 +++++++++++-------- doc/ru/scripting/builtins/libgfx-weather.md | 21 ++++++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/doc/en/scripting/builtins/libgfx-weather.md b/doc/en/scripting/builtins/libgfx-weather.md index 62d8ec33b..669ebc5ea 100644 --- a/doc/en/scripting/builtins/libgfx-weather.md +++ b/doc/en/scripting/builtins/libgfx-weather.md @@ -4,14 +4,17 @@ A library for managing audio/visual weather effects. Weather settings: -| Property | Description | Default | -| ------------ | ------------------------------ | ------- | -| fall | Precipitation (see table 2) | {} | -| clouds | Cloudiness [0.0, 1.0] | 0.0 | -| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 | -| fog_dencity | Fog density | 1.0 | -| fog_curve | Fog curve | 1.0 | -| thunder_rate | Thunder rate [0.0, 1.0] | 0.0 | +| Property | Description | Default | +| ------------- | -------------------------------- | ----------------- | +| fall | Precipitation (see table 2) | {} | +| clouds | Cloudiness [0.0, 1.0] | 0.0 | +| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 | +| fog_dencity | Fog density | 1.0 | +| fog_curve | Fog curve | 1.0 | +| thunder_rate | Thunder rate [0.0, 1.0] | 0.0 | +| sky_tint | Sky color multiplier | {1, 1, 1} | +| clouds_tint | Cloud color multiplier | {1, 1, 1} | +| min_sky_light | Minimum sky-emitted light (moon) | {0.2, 0.25, 0.33} | Precipitation: diff --git a/doc/ru/scripting/builtins/libgfx-weather.md b/doc/ru/scripting/builtins/libgfx-weather.md index add94550b..b51bd58e1 100644 --- a/doc/ru/scripting/builtins/libgfx-weather.md +++ b/doc/ru/scripting/builtins/libgfx-weather.md @@ -4,14 +4,17 @@ Настройки погоды: -| Свойство | Описание | По-умолчанию | -| ------------ | ---------------------------------------- | ------------ | -| fall | Осадки (см. таблица 2) | {} | -| clouds | Облачность [0.0, 1.0] | 0.0 | -| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 | -| fog_dencity | Плотность тумана | 1.0 | -| fog_curve | Кривая тумана | 1.0 | -| thunder_rate | Частота грома [0.0, 1.0] | 0.0 | +| Свойство | Описание | По-умолчанию | +| ------------- | ------------------------------------------ | ----------------- | +| fall | Осадки (см. таблица 2) | {} | +| clouds | Облачность [0.0, 1.0] | 0.0 | +| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 | +| fog_dencity | Плотность тумана | 1.0 | +| fog_curve | Кривая тумана | 1.0 | +| thunder_rate | Частота грома [0.0, 1.0] | 0.0 | +| sky_tint | Множитель цвета неба | {1, 1, 1} | +| clouds_tint | Множитель цвета облаков | {1, 1, 1} | +| min_sky_light | Минимальный свет излучаемый небом (лунный) | {0.2, 0.25, 0.33} | Осадки: @@ -50,4 +53,4 @@ gfx.weather.get_fall_intensity() -> number -- Проверяет, происходит ли в данный момент переключение погоды gfx.weather.is_transition() -> boolean -``` \ No newline at end of file +``` From d6ebdfdd29ab4442a138f5cf77b0549845aad431 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Mar 2026 00:13:12 +0300 Subject: [PATCH 73/73] update doc/*/scripting/ecs.md --- doc/en/scripting/ecs.md | 10 ++++++++++ doc/ru/entity-properties.md | 2 +- doc/ru/scripting/ecs.md | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 79289cbe5..e0c6b69c6 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -119,6 +119,16 @@ body:set_body_type(type: str) body:get_material() -> str -- Sets the body material body:set_material(material: str) + +-- Returns the body's mass +body:get_mass() -> number +-- Sets the body's mass +body:set_mass(mass: number) + +-- Returns the body's elasticity +body:get_elasticity() -> number +-- Sets the body's elasticity +body:set_elasticity(elasticity: number) ``` ### Skeleton diff --git a/doc/ru/entity-properties.md b/doc/ru/entity-properties.md index 522b278a2..50b3b1499 100644 --- a/doc/ru/entity-properties.md +++ b/doc/ru/entity-properties.md @@ -106,7 +106,7 @@ ### Эластичность - *elasticity* -Определяет эластичность сущности (влияет на отскок при столкновении). +Определяет упругость сущности. ### Высота шага - *step-height* diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index 4bc13bc31..50735913a 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -119,6 +119,16 @@ body:set_body_type(type: str) body:get_material() -> str -- Устанавливает материал тела body:set_material(material: str) + +-- Возвращает массу тела +body:get_mass() -> number +-- Устанавливает массу тела +body:set_mass(mass: number) + +-- Возвращает упругость тела +body:get_elasticity() -> number +-- Устанавливает упругость тела +body:set_elasticity(elasticity: number) ``` ### Skeleton