Implement first stage of obstacle detection

master
outfrost 2020-07-16 03:15:20 +02:00
parent c02b8cfab0
commit 79b8f956f7
7 changed files with 181 additions and 21 deletions

View File

@ -78,6 +78,15 @@ Vector scaleVector(Vector vec, float scale) {
vec.z * scale };
}
Vector growVectorNoFlip(Vector vec, float amount) {
float mag = magnitude(vec);
float factor = (mag + amount) / mag;
if (factor < 0.0f) {
factor = 0.0f;
}
return scaleVector(vec, factor);
}
Vector clampMagnitude(Vector vec, float maxMagnitude) {
float m = magnitude(vec);
if (m > maxMagnitude) {

View File

@ -30,6 +30,7 @@ Vector subtractVectors(Vector v1, Vector v2);
Vector crossProduct(Vector v1, Vector v2);
float dotProduct(Vector v1, Vector v2);
Vector scaleVector(Vector vec, float scale);
Vector growVectorNoFlip(Vector vec, float amount);
Vector clampMagnitude(Vector vec, float maxMagnitude);
float magnitude(Vector vec);
Vector applyTransform(Transform transform, Vector vec);

View File

@ -3,6 +3,8 @@
#include "geometry.h"
#include "performance.h"
#include "game/player.h"
float viewportAspectRatio = 1.0f;
const Scene* cameraAnchor;
bool debugScene = false;
@ -67,7 +69,7 @@ static void renderScene(const Scene* scene, const Transform baseTransform) {
glDisable(GL_LIGHTING);
if (debugScene) {
if (debugScene || scene == playerProjectedMovement) {
drawAxes();
}

View File

@ -11,11 +11,9 @@
BlockGrid levelGrid;
static const float BLOCKGRID_CELL_SIZE = 2.5f;
static Block blockEmpty = { .type = BLOCKTYPE_SPACE,
.solid = NULL };
static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE,
static Block blockWall01 = { .type = BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z,
.solid = NULL };
static Transform playerSpawnTransform;
@ -106,8 +104,61 @@ static inline size_t nonNegative(long n) {
return n < 0 ? 0u : n;
}
GridLocation gridLocationFromTransform(Transform transform) {
Vector scaledPos = scaleVector(translationOf(transform), 1.0f / BLOCKGRID_CELL_SIZE);
GridLocation gridLocationFromPosition(Vector pos) {
Vector scaledPos = scaleVector(pos, 1.0f / BLOCKGRID_CELL_SIZE);
return (GridLocation) { .x = nonNegative(scaledPos.x),
.z = nonNegative(scaledPos.z) };
}
Obstacle getObstacles(GridLocation loc) {
Obstacle result = OBSTACLE_NONE;
if (loc.x == 0) {
result |= OBSTACLE_XN | OBSTACLE_XN_ZP | OBSTACLE_XN_ZN;
}
if (loc.x >= levelGrid.width - 1) {
result |= OBSTACLE_XP | OBSTACLE_XP_ZP | OBSTACLE_XP_ZN;
}
if (loc.z == 0) {
result |= OBSTACLE_ZN | OBSTACLE_XP_ZN | OBSTACLE_XN_ZN;
}
if (loc.z >= levelGrid.depth - 1) {
result |= OBSTACLE_ZP | OBSTACLE_XP_ZP | OBSTACLE_XN_ZP;
}
if (!(result & OBSTACLE_XP)
&& getBlockFromGrid(levelGrid, loc.x + 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) {
result |= OBSTACLE_XP;
}
if (!(result & OBSTACLE_XN)
&& getBlockFromGrid(levelGrid, loc.x - 1, loc.z)->type & BLOCKTYPE_OBSTACLE_X) {
result |= OBSTACLE_XN;
}
if (!(result & OBSTACLE_ZP)
&& getBlockFromGrid(levelGrid, loc.x, loc.z + 1)->type & BLOCKTYPE_OBSTACLE_Z) {
result |= OBSTACLE_ZP;
}
if (!(result & OBSTACLE_ZN)
&& getBlockFromGrid(levelGrid, loc.x, loc.z - 1)->type & BLOCKTYPE_OBSTACLE_Z) {
result |= OBSTACLE_ZN;
}
if (!(result & OBSTACLE_XP_ZP)
&& getBlockFromGrid(levelGrid, loc.x + 1, loc.z + 1)->type
& (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
result |= OBSTACLE_XP_ZP;
}
if (!(result & OBSTACLE_XP_ZN)
&& getBlockFromGrid(levelGrid, loc.x + 1, loc.z - 1)->type
& (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
result |= OBSTACLE_XP_ZN;
}
if (!(result & OBSTACLE_XN_ZP)
&& getBlockFromGrid(levelGrid, loc.x - 1, loc.z + 1)->type
& (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
result |= OBSTACLE_XN_ZP;
}
if (!(result & OBSTACLE_XN_ZN)
&& getBlockFromGrid(levelGrid, loc.x - 1, loc.z - 1)->type
& (BLOCKTYPE_OBSTACLE_X | BLOCKTYPE_OBSTACLE_Z)) {
result |= OBSTACLE_XN_ZN;
}
return result;
}

View File

@ -6,13 +6,25 @@
#include "engine/asset.h"
enum BlockType {
BLOCKTYPE_SPACE,
BLOCKTYPE_OBSTACLE_X,
BLOCKTYPE_OBSTACLE_Z,
BLOCKTYPE_OBSTACLE
BLOCKTYPE_SPACE = 0,
BLOCKTYPE_OBSTACLE_X = 1 << 0,
BLOCKTYPE_OBSTACLE_Z = 1 << 1
};
enum Obstacle {
OBSTACLE_NONE = 0,
OBSTACLE_XP = 1 << 0,
OBSTACLE_XN = 1 << 1,
OBSTACLE_ZP = 1 << 2,
OBSTACLE_ZN = 1 << 3,
OBSTACLE_XP_ZP = 1 << 4,
OBSTACLE_XP_ZN = 1 << 5,
OBSTACLE_XN_ZP = 1 << 6,
OBSTACLE_XN_ZN = 1 << 7
};
typedef enum BlockType BlockType;
typedef enum Obstacle Obstacle;
typedef struct Block Block;
typedef struct BlockGrid BlockGrid;
typedef struct GridLocation GridLocation;
@ -33,11 +45,14 @@ struct GridLocation {
size_t z;
};
static const float BLOCKGRID_CELL_SIZE = 2.5f;
extern BlockGrid levelGrid;
void initLevel();
void startLevel();
GridLocation gridLocationFromTransform(Transform transform);
GridLocation gridLocationFromPosition(Vector pos);
Obstacle getObstacles(GridLocation loc);
static inline Block* getBlockFromGrid(BlockGrid grid, size_t x, size_t z) {
return grid.blocks[(z * grid.width) + x];
@ -47,4 +62,8 @@ static inline void setBlockInGrid(BlockGrid grid, size_t x, size_t z, Block* blo
grid.blocks[(z * grid.width) + x] = block;
}
static inline float cellBoundaryCoord(size_t cellIndex) {
return cellIndex * BLOCKGRID_CELL_SIZE;
}
#endif // LEVEL_H_

View File

@ -5,9 +5,11 @@
#include "level.h"
static const float movementSpeed = 2.5f;
static const float movementSpeed = 0.5f;
static const float collisionRadius = 0.5f;
Scene* playerCharacter;
Scene* playerProjectedMovement;
static Transform screenToWorldMovementTransform;
static Vector worldMovementUp;
static Vector worldMovementDown;
@ -32,11 +34,14 @@ void initPlayer() {
playerCharacter = newScene();
cameraAnchor = playerCharacter;
playerCharacter->solid = importSolid("assets/playercharacter.3ds");
playerProjectedMovement = newScene();
}
void spawnPlayer(Transform transform) {
playerCharacter->transform = transform;
reparentScene(playerCharacter, currentScene);
reparentScene(playerProjectedMovement, currentScene);
}
void updatePlayer(float delta) {
@ -68,22 +73,94 @@ static void movePlayer(Vector direction, float delta) {
direction = clampMagnitude(direction, 1.0f);
Vector displacement = scaleVector(direction, delta * movementSpeed);
{
Vector displacement = scaleVector(direction, 0.006944f * movementSpeed * 1000.0f);
//GridLocation location = gridLocationFromTransform(playerCharacter->transform);
playerProjectedMovement->transform = playerCharacter->transform;
if (displacement.x >= 0) {
// need to test +X edge
Vector initialPosition = translationOf(playerCharacter->transform);
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 (displacement.x <= 0) {
// need to test -X edge
if (obstacle & OBSTACLE_XN) {
obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN);
}
if (displacement.z >= 0) {
// need to test +Z edge
if (obstacle & OBSTACLE_ZP) {
obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP);
}
if (displacement.z <= 0) {
// need to test -Z edge
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
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, displacement);
}

View File

@ -15,6 +15,7 @@ enum Direction {
typedef enum Direction Direction;
extern Scene* playerCharacter;
extern Scene* playerProjectedMovement;
void initPlayer();
void spawnPlayer(Transform transform);