Implement sliding and crossing grid cell boundaries

This commit is contained in:
outfrost 2020-09-21 01:05:04 +02:00
parent ab9db26b9f
commit eb0ee96f2f
3 changed files with 153 additions and 73 deletions

View File

@ -62,7 +62,11 @@ void rotate(Transform* transform, Vector axis, float angle) {
*transform); *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 }; 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); float m = magnitude(vec);
return (Vector) { vec.x / m, vec.y / m, vec.z / m }; 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));
}

View File

@ -33,6 +33,7 @@ Transform identity();
Transform multiply(Transform t1, Transform t2); Transform multiply(Transform t1, Transform t2);
void translate(Transform* transform, Vector vec); void translate(Transform* transform, Vector vec);
void rotate(Transform* transform, Vector axis, float angle); void rotate(Transform* transform, Vector axis, float angle);
Vector zeroVector();
Vector addVectors(Vector v1, Vector v2); Vector addVectors(Vector v1, Vector v2);
Vector subtractVectors(Vector v1, Vector v2); Vector subtractVectors(Vector v1, Vector v2);
Vector crossProduct(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 applyTransform(Transform transform, Vector vec);
Vector translationOf(Transform transform); Vector translationOf(Transform transform);
Vector normalized(Vector vec); Vector normalized(Vector vec);
float clamp(float x, float lower, float upper);
#endif // ENGINE_GEOMETRY_H_ #endif // ENGINE_GEOMETRY_H_

View File

@ -9,6 +9,7 @@
#include "player.h" #include "player.h"
#include "engine/asset.h" #include "engine/asset.h"
#include "engine/logger.h"
#include "engine/render.h" #include "engine/render.h"
#include "level.h" #include "level.h"
@ -53,7 +54,7 @@ void spawnPlayer(Transform transform) {
} }
void updatePlayer(float delta) { void updatePlayer(float delta) {
Vector direction = { 0.0f, 0.0f, 0.0f }; Vector direction = zeroVector();
if (movementDirection & DIRECTION_UP) { if (movementDirection & DIRECTION_UP) {
direction = addVectors(direction, worldMovementUp); direction = addVectors(direction, worldMovementUp);
} }
@ -90,83 +91,152 @@ Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f
Vector position = initialPosition; Vector position = initialPosition;
GridLocation location = gridLocationFromPosition(position); GridLocation location = gridLocationFromPosition(position);
Obstacle obstacle = getObstacles(location); bool enteredNewCell = true;
// Eliminate redundant corner checks while (enteredNewCell) {
if (obstacle & OBSTACLE_XP) { enteredNewCell = false;
obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN); Obstacle obstacle = getObstacles(location);
}
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); // Eliminate redundant corner checks
float edgeXn = cellBoundaryCoord(location.x); if (obstacle & OBSTACLE_XP) {
float edgeZp = cellBoundaryCoord(location.z + 1); obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN);
float edgeZn = cellBoundaryCoord(location.z); }
float distanceXp = edgeXp - position.x; if (obstacle & OBSTACLE_XN) {
if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius; obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN);
float distanceXn = edgeXn - position.x; }
if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius; if (obstacle & OBSTACLE_ZP) {
float distanceZp = edgeZp - position.z; obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP);
if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius; }
float distanceZn = edgeZn - position.z; if (obstacle & OBSTACLE_ZN) {
if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius; obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN);
}
// Check all edges for intersecting already float edgeXp = cellBoundaryCoord(location.x + 1);
if (distanceXp < 0.0f) { float edgeXn = cellBoundaryCoord(location.x);
position.x += distanceXp; float edgeZp = cellBoundaryCoord(location.z + 1);
displacement = growVectorNoFlip(displacement, distanceXp); float edgeZn = cellBoundaryCoord(location.z);
distanceXp = 0.0f; float distanceXp = edgeXp - position.x;
} if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius;
if (distanceXn > 0.0f) { float distanceXn = edgeXn - position.x;
position.x += distanceXn; if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius;
displacement = growVectorNoFlip(displacement, - distanceXn); float distanceZp = edgeZp - position.z;
distanceXn = 0.0f; if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius;
} float distanceZn = edgeZn - position.z;
if (distanceZp < 0.0f) { if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius;
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 // Check all edges for intersecting already
float dx = displacement.x; if (distanceXp < 0.0f) {
float dz = displacement.z; position.x += distanceXp;
if (dx > distanceXp) { displacement = growVectorNoFlip(displacement, distanceXp);
dz *= distanceXp / dx; distanceXp = 0.0f;
dx = distanceXp; }
// Might need a flag that we've reached a limit in X+? 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 (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(&playerProjectedMovement->transform, subtractVectors(position, initialPosition));
//translate(&playerCharacter->transform, subtractVectors(position, initialPosition));
} }