157 lines
5.6 KiB
C
157 lines
5.6 KiB
C
// voxelThing.c
|
|
// A voxel chunk rendering doodad written in C with Raylib
|
|
|
|
// External libraries
|
|
#include "raylib.h"
|
|
#include "raymath.h"
|
|
#include "rlgl.h"
|
|
#include "stdio.h"
|
|
|
|
// Project files
|
|
#include "chunkStructures.h"
|
|
#include "chunkRenderer.h"
|
|
#include "blockTypes.h"
|
|
#include "playerController.h"
|
|
#include "chunkGenerator.h"
|
|
|
|
float cameraYaw = 0;
|
|
float cameraPitch = 0;
|
|
|
|
const char* GetCompassDirection(float yaw) {
|
|
yaw = fmodf(yaw * RAD2DEG + 360.0f, 360.0f);
|
|
if (yaw < 22.5f || yaw >= 337.5f) return "South (+Z)";
|
|
if (yaw < 67.5f) return "Southwest (+X+Z)";
|
|
if (yaw < 112.5f) return "West (+X)";
|
|
if (yaw < 157.5f) return "Northwest (+X-Z)";
|
|
if (yaw < 202.5f) return "North (-Z)";
|
|
if (yaw < 247.5f) return "Northeast (-X-Z)";
|
|
if (yaw < 292.5f) return "East (-X)";
|
|
return "Southeast (-X+Z)";
|
|
}
|
|
|
|
int main(void) {
|
|
// --- Screen setup ---
|
|
const int screenWidth = 800;
|
|
const int screenHeight = 600;
|
|
InitWindow(screenWidth, screenHeight, "VoxelThing");
|
|
DisableCursor(); // Lock mouse to window for FPS-style camera
|
|
|
|
// --- World generation ---
|
|
Chunk chunk;
|
|
GenerateFlatChunk(&chunk);
|
|
PlaceTreeAt(&chunk, 8, 7, 8);
|
|
Mesh chunkMesh = GenerateChunkMesh(&chunk);
|
|
|
|
// --- Load textures and materials ---
|
|
Texture2D atlas = LoadTexture("assets/TextureAtlas.png");
|
|
Material mat = LoadMaterialDefault();
|
|
mat.maps[MATERIAL_MAP_DIFFUSE].texture = atlas;
|
|
|
|
// --- Initialize camera ---
|
|
Camera3D camera = { 0 };
|
|
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Initial camera position
|
|
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Looking toward origin
|
|
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Y-up world
|
|
camera.fovy = 45.0f; // Field of view
|
|
camera.projection = CAMERA_PERSPECTIVE; // Use perspective projection
|
|
|
|
SetTargetFPS(60);
|
|
|
|
// --- Main game loop ---
|
|
while (!WindowShouldClose()) {
|
|
BeginDrawing();
|
|
ClearBackground(RAYWHITE);
|
|
|
|
// --- Update camera and direction ---
|
|
|
|
UpdateFreeCamera(&camera, 10.0f, &cameraYaw, &cameraPitch); // Move camera with user input
|
|
|
|
|
|
// --- Raycasting from screen center ---
|
|
Vector2 screenCenter = { screenWidth / 2.0f, screenHeight / 2.0f };
|
|
// This is where we grab the ray...
|
|
// Ray ray = GetMouseRay(screenCenter, camera);
|
|
Vector3 camDir = {
|
|
cosf(cameraPitch) * sinf(cameraYaw),
|
|
sinf(cameraPitch),
|
|
cosf(cameraPitch) * cosf(cameraYaw)
|
|
};
|
|
|
|
Ray ray = {
|
|
.position = camera.position,
|
|
.direction = Vector3Normalize(camDir)
|
|
};
|
|
RaycastHit hit = RaycastChunk(&chunk, ray.position, ray.direction, 10.0f);
|
|
|
|
// --- Begin 3D rendering ---
|
|
BeginMode3D(camera);
|
|
|
|
DrawMesh(chunkMesh, mat, MatrixIdentity());
|
|
|
|
if (hit.hit) {
|
|
// Draw a wireframe cube where the ray hit
|
|
DrawCubeWires(Vector3Add(hit.position, (Vector3){0.5f, 0.5f, 0.5f}), 1.02f, 1.02f, 1.02f, RED);
|
|
DrawFaceHighlight(hit.position, hit.normal); // Highlight the specific face hit
|
|
}
|
|
// Draw debug ray.
|
|
camDir = Vector3Normalize(Vector3Subtract(camera.target, camera.position));
|
|
Vector3 rayEnd = Vector3Add(camera.position, Vector3Scale(camDir, 10.0f));
|
|
DrawLine3D(camera.position, rayEnd, PURPLE);
|
|
|
|
Vector3 hitPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, hit.t));
|
|
DrawCubeWires(hitPoint, 0.05f, 0.05f, 0.05f, RED);
|
|
|
|
EndMode3D();
|
|
|
|
Vector3 testPoint = Vector3Add(camera.position, Vector3Scale(camDir, 2.0f));
|
|
Vector2 projected = GetWorldToScreen(testPoint, camera);
|
|
DrawCircleV(projected, 4, RED);
|
|
|
|
// --- Draw crosshair in screen center ---
|
|
DrawLine(screenWidth/2 - 5, screenHeight/2, screenWidth/2 + 5, screenHeight/2, DARKGRAY);
|
|
DrawLine(screenWidth/2, screenHeight/2 - 5, screenWidth/2, screenHeight/2 + 5, DARKGRAY);
|
|
|
|
// -- Draw debug info ---
|
|
DrawText(TextFormat("Facing: %s", GetCompassDirection(cameraYaw)), 10, 10, 20, DARKGRAY);
|
|
DrawText(TextFormat("Yaw: %.1f° Pitch: %.1f°", cameraYaw * RAD2DEG, cameraPitch * RAD2DEG), 10, 30, 20, GRAY);
|
|
|
|
EndDrawing();
|
|
|
|
// TODO: Shoudn't all this block handling be handled in playerController.c?
|
|
|
|
// --- Handle block removal (left click) ---
|
|
if (hit.hit && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
|
chunk.blocks[(int)hit.position.x][(int)hit.position.y][(int)hit.position.z].type = BLOCK_AIR;
|
|
chunkMesh = GenerateChunkMesh(&chunk); // Rebuild mesh after change
|
|
}
|
|
|
|
// --- Handle block placement (right click) ---
|
|
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);
|
|
|
|
// Bounds check
|
|
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 = BLOCK_SAND;
|
|
chunkMesh = GenerateChunkMesh(&chunk);
|
|
|
|
printf("Hit at (%f %f %f), normal (%f %f %f), placing at (%d %d %d)\n",
|
|
hit.position.x, hit.position.y, hit.position.z,
|
|
hit.normal.x, hit.normal.y, hit.normal.z,
|
|
px, py, pz);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Cleanup ---
|
|
UnloadTexture(atlas);
|
|
UnloadMesh(chunkMesh);
|
|
UnloadMaterial(mat);
|
|
CloseWindow();
|
|
return 0;
|
|
}
|