From eb0ee96f2ffdf6bd18116609a1372e9f9ae96e53 Mon Sep 17 00:00:00 2001 From: outfrost Date: Mon, 21 Sep 2020 01:05:04 +0200 Subject: [PATCH] Implement sliding and crossing grid cell boundaries --- src/engine/geometry.c | 10 +- src/engine/geometry.h | 2 + src/game/player.c | 222 +++++++++++++++++++++++++++--------------- 3 files changed, 157 insertions(+), 77 deletions(-) diff --git a/src/engine/geometry.c b/src/engine/geometry.c index 3809d99..660a57b 100644 --- a/src/engine/geometry.c +++ b/src/engine/geometry.c @@ -62,7 +62,11 @@ void rotate(Transform* transform, Vector axis, float angle) { *transform); } -Vector addVectors(Vector v1, Vector v2){ +Vector zeroVector() { + return (Vector) { 0.0f, 0.0f, 0.0f }; +} + +Vector addVectors(Vector v1, Vector v2) { return (Vector) { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; } @@ -130,3 +134,7 @@ Vector normalized(Vector vec) { float m = magnitude(vec); return (Vector) { vec.x / m, vec.y / m, vec.z / m }; } + +float clamp(float x, float lower, float upper) { + return fmax(lower, fmin(upper, x)); +} diff --git a/src/engine/geometry.h b/src/engine/geometry.h index 24ffccf..4b0d484 100644 --- a/src/engine/geometry.h +++ b/src/engine/geometry.h @@ -33,6 +33,7 @@ Transform identity(); Transform multiply(Transform t1, Transform t2); void translate(Transform* transform, Vector vec); void rotate(Transform* transform, Vector axis, float angle); +Vector zeroVector(); Vector addVectors(Vector v1, Vector v2); Vector subtractVectors(Vector v1, Vector v2); Vector crossProduct(Vector v1, Vector v2); @@ -44,5 +45,6 @@ float magnitude(Vector vec); Vector applyTransform(Transform transform, Vector vec); Vector translationOf(Transform transform); Vector normalized(Vector vec); +float clamp(float x, float lower, float upper); #endif // ENGINE_GEOMETRY_H_ diff --git a/src/game/player.c b/src/game/player.c index 6731107..861b655 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -9,6 +9,7 @@ #include "player.h" #include "engine/asset.h" +#include "engine/logger.h" #include "engine/render.h" #include "level.h" @@ -53,7 +54,7 @@ void spawnPlayer(Transform transform) { } void updatePlayer(float delta) { - Vector direction = { 0.0f, 0.0f, 0.0f }; + Vector direction = zeroVector(); if (movementDirection & DIRECTION_UP) { direction = addVectors(direction, worldMovementUp); } @@ -90,83 +91,152 @@ Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f Vector position = initialPosition; GridLocation location = gridLocationFromPosition(position); - Obstacle obstacle = getObstacles(location); - - // Eliminate redundant corner checks - if (obstacle & OBSTACLE_XP) { - obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN); - } - if (obstacle & OBSTACLE_XN) { - obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN); - } - if (obstacle & OBSTACLE_ZP) { - obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP); - } - if (obstacle & OBSTACLE_ZN) { - obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN); - } - - float edgeXp = cellBoundaryCoord(location.x + 1); - float edgeXn = cellBoundaryCoord(location.x); - float edgeZp = cellBoundaryCoord(location.z + 1); - float edgeZn = cellBoundaryCoord(location.z); - float distanceXp = edgeXp - position.x; - if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius; - float distanceXn = edgeXn - position.x; - if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius; - float distanceZp = edgeZp - position.z; - if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius; - float distanceZn = edgeZn - position.z; - if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius; - - // Check all edges for intersecting already - if (distanceXp < 0.0f) { - position.x += distanceXp; - displacement = growVectorNoFlip(displacement, distanceXp); - distanceXp = 0.0f; - } - if (distanceXn > 0.0f) { - position.x += distanceXn; - displacement = growVectorNoFlip(displacement, - distanceXn); - distanceXn = 0.0f; - } - if (distanceZp < 0.0f) { - position.z += distanceZp; - displacement = growVectorNoFlip(displacement, distanceZp); - distanceZp = 0.0f; + bool enteredNewCell = true; + + while (enteredNewCell) { + enteredNewCell = false; + Obstacle obstacle = getObstacles(location); + + // Eliminate redundant corner checks + if (obstacle & OBSTACLE_XP) { + obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN); + } + if (obstacle & OBSTACLE_XN) { + obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN); + } + if (obstacle & OBSTACLE_ZP) { + obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP); + } + if (obstacle & OBSTACLE_ZN) { + obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN); + } + + float edgeXp = cellBoundaryCoord(location.x + 1); + float edgeXn = cellBoundaryCoord(location.x); + float edgeZp = cellBoundaryCoord(location.z + 1); + float edgeZn = cellBoundaryCoord(location.z); + float distanceXp = edgeXp - position.x; + if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius; + float distanceXn = edgeXn - position.x; + if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius; + float distanceZp = edgeZp - position.z; + if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius; + float distanceZn = edgeZn - position.z; + if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius; + + // Check all edges for intersecting already + if (distanceXp < 0.0f) { + position.x += distanceXp; + displacement = growVectorNoFlip(displacement, distanceXp); + distanceXp = 0.0f; + } + if (distanceXn > 0.0f) { + position.x += distanceXn; + displacement = growVectorNoFlip(displacement, - distanceXn); + distanceXn = 0.0f; + } + if (distanceZp < 0.0f) { + position.z += distanceZp; + displacement = growVectorNoFlip(displacement, distanceZp); + distanceZp = 0.0f; + } + if (distanceZn > 0.0f) { + position.z += distanceZn; + displacement = growVectorNoFlip(displacement, - distanceZn); + distanceZn = 0.0f; + } + + // Calculate direct movement limits + Vector displacementToLimit = displacement; + bool reachedXp = false; + bool reachedXn = false; + bool reachedZp = false; + bool reachedZn = false; + if (displacementToLimit.x > distanceXp) { + displacementToLimit = scaleVector( + displacementToLimit, distanceXp / displacementToLimit.x); + reachedXp = true; + } + if (displacementToLimit.x < distanceXn) { + displacementToLimit = scaleVector( + displacementToLimit, distanceXn / displacementToLimit.x); + reachedXn = true; + } + if (displacementToLimit.z > distanceZp) { + displacementToLimit = scaleVector( + displacementToLimit, distanceZp / displacementToLimit.z); + reachedZp = true; + } + if (displacementToLimit.z < distanceZn) { + displacementToLimit = scaleVector( + displacementToLimit, distanceZn / displacementToLimit.z); + reachedZn = true; + } + // This is how far we can move until we collide or leave the grid cell + position = addVectors(position, displacementToLimit); + displacement = subtractVectors(displacement, displacementToLimit); + // Update distances + distanceXp -= displacementToLimit.x; + distanceXn -= displacementToLimit.x; + distanceZp -= displacementToLimit.z; + distanceZn -= displacementToLimit.z; + + // Resolve slides and crossing cell boundaries + // in reverse order to direct movement limits, because + // we only want to cross the closest cell boundary. + if (reachedZn) { + if (obstacle & OBSTACLE_ZN) { + float dx = clamp(displacement.x, distanceXn, distanceXp); + position.x += dx; + displacement = scaleVector(displacement, 1.0f - (dx / displacement.x)); + } + else { + location.z -= 1; + enteredNewCell = true; + } + } + + if (reachedZp) { + if (obstacle & OBSTACLE_ZP) { + float dx = clamp(displacement.x, distanceXn, distanceXp); + position.x += dx; + displacement = scaleVector(displacement, 1.0f - (dx / displacement.x)); + } + else if (!enteredNewCell) { + location.z += 1; + enteredNewCell = true; + } + } + + if (reachedXn) { + if (obstacle & OBSTACLE_XN) { + float dz = clamp(displacement.z, distanceZn, distanceZp); + position.z += dz; + displacement = scaleVector(displacement, 1.0f - (dz / displacement.z)); + } + else if (!enteredNewCell) { + location.x -= 1; + enteredNewCell = true; + } + } + + if (reachedXp) { + if (obstacle & OBSTACLE_XP) { + // Slide + float dz = clamp(displacement.z, distanceZn, distanceZp); + position.z += dz; + displacement = scaleVector(displacement, 1.0f - (dz / displacement.z)); + } + else if (!enteredNewCell) { + // Enter new grid cell + location.x += 1; + enteredNewCell = true; + } + } } - if (distanceZn > 0.0f) { - position.z += distanceZn; - displacement = growVectorNoFlip(displacement, - distanceZn); - distanceZn = 0.0f; - } - - // Calculate direct movement limits - float dx = displacement.x; - float dz = displacement.z; - if (dx > distanceXp) { - dz *= distanceXp / dx; - dx = distanceXp; - // Might need a flag that we've reached a limit in X+? - } - if (dx < distanceXn) { - dz *= distanceXn / dx; - dx = distanceXn; - // ? - } - if (dz > distanceZp) { - dx *= distanceZp / dz; - dz = distanceZp; - // ? - } - if (dz < distanceZn) { - dx *= distanceZn / dz; - dz = distanceZn; - // ? - } - // This is how far we can move until we collide or leave the grid cell - position = addVectors(position, (Vector) { dx, 0.0f, dz }); translate(&playerProjectedMovement->transform, subtractVectors(position, initialPosition)); + + //translate(&playerCharacter->transform, subtractVectors(position, initialPosition)); } -- 2.44.0