voxelThing/source/playerController.c

153 lines
5.9 KiB
C

// playerController.c
#include "raylib.h"
#include "raymath.h"
#include "playerController.h"
#include "blockTypes.h"
#include <math.h>
void UpdatePlayer(Player *player) {
Vector2 mouseDelta = GetMouseDelta();
player->playerOrientation.x += mouseDelta.x * -0.002f;
player->playerOrientation.y += mouseDelta.y * -0.002f;
float limit = PI / 1.8f;
if (player->playerOrientation.y > limit) player->playerOrientation.y = limit;
if (player->playerOrientation.y < -limit) player->playerOrientation.y = -limit;
float yaw = player->playerOrientation.x;
float pitch = player->playerOrientation.y;
player->forward = (Vector3){
cosf(pitch) * sinf(yaw),
sinf(pitch),
cosf(pitch) * cosf(yaw)
};
player->right = (Vector3){
sinf(yaw - PI/2.0f),
0.0f,
cosf(yaw - PI/2.0f)
};
Vector3 movement = {0};
if (IsKeyDown(KEY_W)) movement = Vector3Add(movement, player->forward);
if (IsKeyDown(KEY_S)) movement = Vector3Subtract(movement, player->forward);
if (IsKeyDown(KEY_A)) movement = Vector3Subtract(movement, player->right);
if (IsKeyDown(KEY_D)) movement = Vector3Add(movement, player->right);
if (IsKeyDown(KEY_SPACE)) movement.y += 1.0f;
if (IsKeyDown(KEY_LEFT_SHIFT)) movement.y -= 1.0f;
if (Vector3Length(movement) > 0.0f)
movement = Vector3Scale(Vector3Normalize(movement), player->moveSpeed * GetFrameTime());
player->mapPosition = Vector3Add(player->mapPosition, movement);
player->camera.position = player->mapPosition;
player->camera.target = Vector3Add(player->mapPosition, player->forward);
}
// An implementation of DDA (digital differential analyzer), steps through each voxel boundary along a ray cast from origin along direction to maxDistance
RaycastHit RaycastChunk(const Chunk *chunk, Vector3 origin, Vector3 direction, float maxDistance) {
RaycastHit result = {0}; // Initialize result: no hit, zeroed values
direction = Vector3Normalize(direction); // Ensure direction is a unit vector
// Nudge the origin slightly to avoid precision issues at block boundaries
origin = Vector3Add(origin, Vector3Scale(direction, 0.001f));
// Determine the next voxel boundary to cross along each axis
float nextX = (direction.x >= 0) ? ceilf(origin.x) : floorf(origin.x);
float nextY = (direction.y >= 0) ? ceilf(origin.y) : floorf(origin.y);
float nextZ = (direction.z >= 0) ? ceilf(origin.z) : floorf(origin.z);
// Get integer voxel coordinates for current position
int x = (int)floorf(origin.x);
int y = (int)floorf(origin.y);
int z = (int)floorf(origin.z);
// Determine step direction: +1 or -1 along each axis
int stepX = (direction.x >= 0) ? 1 : -1;
int stepY = (direction.y >= 0) ? 1 : -1;
int stepZ = (direction.z >= 0) ? 1 : -1;
// How far to travel along the ray to cross a voxel in each axis
float tDeltaX = (direction.x == 0) ? INFINITY : fabsf(1.0f / direction.x);
float tDeltaY = (direction.y == 0) ? INFINITY : fabsf(1.0f / direction.y);
float tDeltaZ = (direction.z == 0) ? INFINITY : fabsf(1.0f / direction.z);
// Distance from origin to the first voxel boundary (for each axis)
float tMaxX = (direction.x == 0) ? INFINITY : (nextX - origin.x) / direction.x;
float tMaxY = (direction.y == 0) ? INFINITY : (nextY - origin.y) / direction.y;
float tMaxZ = (direction.z == 0) ? INFINITY : (nextZ - origin.z) / direction.z;
float t = 0.0f; // Total traveled distance along the ray
Vector3 lastNormal = {0}; // Which face was entered (used for highlighting/interactions)
// Walk the ray through the voxel grid until we exceed maxDistance
while (t < maxDistance) {
// Check if the current voxel is inside the chunk bounds
if (x >= 0 && y >= 0 && z >= 0 &&
x < CHUNK_SIZE_X && y < CHUNK_SIZE_Y && z < CHUNK_SIZE_Z) {
int blockID = chunk->blocks[x][y][z].type;
// If it's not air, we hit something!
if (blockID != BLOCK_AIR) {
result.hit = true;
result.blockID = blockID;
result.position = (Vector3){x, y, z};
result.normal = lastNormal;
result.t = t;
return result;
}
}
// Move to the next voxel along the smallest tMax (i.e., the closest boundary)
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
x += stepX;
t = tMaxX;
tMaxX += tDeltaX;
lastNormal = (Vector3){-stepX, 0, 0}; // Normal points opposite the ray step
} else if (tMaxY < tMaxZ) {
y += stepY;
t = tMaxY;
tMaxY += tDeltaY;
lastNormal = (Vector3){0, -stepY, 0};
} else {
z += stepZ;
t = tMaxZ;
tMaxZ += tDeltaZ;
lastNormal = (Vector3){0, 0, -stepZ};
}
}
// If no block was hit, return default (no hit)
return result;
}
RaycastHit GetPlayerRaycastHit(Player *player, Chunk *chunk, float maxDistance) {
Ray ray = {
.position = player->mapPosition,
.direction = Vector3Normalize(player->forward)
};
return RaycastChunk(chunk, ray.position, ray.direction, maxDistance);
}
void HandleBlockInteraction(Player *player, Chunk *chunk, RaycastHit hit, int blockSelection) {
if (hit.hit && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
chunk->blocks[(int)hit.position.x][(int)hit.position.y][(int)hit.position.z].type = BLOCK_AIR;
}
if (hit.hit && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
int px = (int)(hit.position.x + hit.normal.x);
int py = (int)(hit.position.y + hit.normal.y);
int pz = (int)(hit.position.z + hit.normal.z);
if (px >= 0 && px < CHUNK_SIZE_X &&
py >= 0 && py < CHUNK_SIZE_Y &&
pz >= 0 && pz < CHUNK_SIZE_Z) {
chunk->blocks[px][py][pz].type = blockSelection;
}
}
}