Added player struct. Multiple chunks, and player interaction updates to support them.
This commit is contained in:
parent
77cba9260a
commit
06b9a621ef
@ -1,7 +1,7 @@
|
||||
# voxelThing
|
||||
Jake's Raylib Minecraft Clone
|
||||
|
||||
Implements a voxel chunk renderer. Chunks are stored as a 3D array of block struct elements which define block types and later other block data. The chunk is converted to a mesh such that only faces exposed to air blocks or the chunk boarders are rendered.
|
||||
Renders a voxel world in chunks.
|
||||
|
||||
Code is rough and messy and not much is implemented yet.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// blockTypes.h
|
||||
#ifndef BLOCK_TYPES_H
|
||||
#define BLOCKTYPES_H
|
||||
#define BLOCK_TYPES_H
|
||||
|
||||
// Definitions for Block IDs (notes are texture atlas indicies)
|
||||
#define BLOCK_AIR 0 // No texture.
|
||||
|
||||
11
include/chunkIO.h
Normal file
11
include/chunkIO.h
Normal file
@ -0,0 +1,11 @@
|
||||
// chunkIO.h
|
||||
#ifndef CHUNK_IO_H
|
||||
#define CHUNK_IO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "chunkStructures.h"
|
||||
|
||||
bool SaveChunk(const Chunk *chunk);
|
||||
bool LoadChunk(Chunk *chunk);
|
||||
|
||||
#endif
|
||||
@ -18,6 +18,9 @@ typedef struct {
|
||||
Block blocks[CHUNK_SIZE_X][CHUNK_SIZE_Y][CHUNK_SIZE_Z];
|
||||
Mesh mesh; // Owned by the chunk, valid only at runtime
|
||||
bool hasMesh; //
|
||||
bool hasChanged; // Flag that determines if chunk needs to be saved on unload
|
||||
int x;
|
||||
int z;
|
||||
} Chunk;
|
||||
|
||||
// 6 directions for checking neighbors: +/-X, +/-Y, +/-Z
|
||||
@ -36,10 +39,6 @@ int IsBlockFaceExposed(Chunk *chunk, int x, int y, int z, int dir);
|
||||
// Function that places a tree dumbly.
|
||||
void PlaceTreeAt(Chunk *chunk, int x, int y, int z) ;
|
||||
|
||||
// Save chunk to disk.
|
||||
bool SaveChunk(const Chunk *chunk, const char *filename);
|
||||
|
||||
// Load chunk from disk.
|
||||
bool LoadChunk(Chunk *chunk, const char *filename);
|
||||
void InitChunk(Chunk *chunk, int chunkX, int chunkZ);
|
||||
|
||||
#endif
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "raylib.h"
|
||||
#include "chunkStructures.h"
|
||||
#include "world.h"
|
||||
|
||||
typedef struct {
|
||||
Vector3 mapPosition; // Player's world position (camera position)
|
||||
@ -19,14 +20,20 @@ void UpdatePlayer(Player *player);
|
||||
typedef struct {
|
||||
bool hit;
|
||||
Vector3 position;
|
||||
int hitBlockX, hitBlockY, hitBlockZ;
|
||||
Vector3 normal;
|
||||
int blockID;
|
||||
float t;
|
||||
int chunkX;
|
||||
int chunkZ;
|
||||
} RaycastHit;
|
||||
|
||||
RaycastHit RaycastChunk(const Chunk *chunk, Vector3 origin, Vector3 direction, float maxDistance);
|
||||
|
||||
RaycastHit GetPlayerRaycastHit(Player *player, Chunk *chunk, float maxDistance);
|
||||
RaycastHit GetPlayerRaycastHit(Player *player, World *world, float maxDistance);
|
||||
|
||||
// Osolete with multichunk worlds.
|
||||
//RaycastHit GetPlayerRaycastHit(Player *player, Chunk *chunk, float maxDistance);
|
||||
|
||||
void UpdateFreeCamera(Camera3D *cam, float speed, float *yawOut, float *pitchOut);
|
||||
|
||||
|
||||
22
include/world.h
Normal file
22
include/world.h
Normal file
@ -0,0 +1,22 @@
|
||||
// world.h
|
||||
#ifndef WORLD_H
|
||||
#define WORLD_H
|
||||
|
||||
#include "chunkStructures.h"
|
||||
|
||||
#define WORLD_SIZE_X 16
|
||||
#define WORLD_SIZE_Z 16
|
||||
|
||||
// World is currently a flat grid of chunks on the X-Z plane
|
||||
typedef struct {
|
||||
Chunk *chunks[WORLD_SIZE_X][WORLD_SIZE_Z];
|
||||
} World;
|
||||
|
||||
void InitWorld(World *world);
|
||||
void FreeWorld(World *world);
|
||||
void UpdateWorld(World *world);
|
||||
Chunk *GetChunk(World *world, int chunkX, int chunkZ);
|
||||
|
||||
Chunk *GetChunkContainingBlock(World *world, int wx, int wz);
|
||||
|
||||
#endif
|
||||
@ -5,7 +5,7 @@
|
||||
#include "chunkGenerator.h"
|
||||
#include "blockTypes.h"
|
||||
|
||||
// Fill a chunk with normalish Minecraft style flatworld terrain. A few layers of stone on the bottom, a few layers of dirt, and a layer of grass on top.
|
||||
// Fill a chunk with normalish Minecraft style flatworld terrain. Some layers of stone on the bottom, a few layers of dirt, and a layer of grass on top.
|
||||
void GenerateFlatChunk(Chunk *chunk) {
|
||||
for (int x = 0; x < CHUNK_SIZE_X; x++) {
|
||||
for (int z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||
|
||||
30
source/chunkIO.c
Normal file
30
source/chunkIO.c
Normal file
@ -0,0 +1,30 @@
|
||||
// chunkIO.c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "chunkIO.h"
|
||||
|
||||
bool SaveChunk(const Chunk *chunk) {
|
||||
char filename[128];
|
||||
snprintf(filename, sizeof(filename), "saves/chunk-%d-%d.dat", chunk->x, chunk->z);
|
||||
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (!file) return false;
|
||||
|
||||
fwrite(chunk->blocks, sizeof(Block), CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z, file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadChunk(Chunk *chunk) {
|
||||
char filename[128];
|
||||
snprintf(filename, sizeof(filename), "saves/chunk-%d-%d.dat", chunk->x, chunk->z);
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) return false;
|
||||
|
||||
fread(chunk->blocks, sizeof(Block), CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z, file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
@ -10,20 +10,18 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
bool SaveChunk(const Chunk *chunk, const char *filename) {
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) return false;
|
||||
size_t written = fwrite(chunk->blocks, sizeof(Block), CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z, fp);
|
||||
fclose(fp);
|
||||
return written == CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z;
|
||||
}
|
||||
|
||||
bool LoadChunk(Chunk *chunk, const char *filename) {
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (!fp) return false;
|
||||
size_t read = fread(chunk->blocks, sizeof(Block), CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z, fp);
|
||||
fclose(fp);
|
||||
return read == CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z;
|
||||
void InitChunk(Chunk *chunk, int chunkX, int chunkZ) {
|
||||
chunk->mesh = (Mesh){ 0 };
|
||||
chunk->x = chunkX;
|
||||
chunk->z = chunkZ;
|
||||
// Zero out block data
|
||||
for (int x = 0; x < CHUNK_SIZE_X; x++) {
|
||||
for (int y = 0; y < CHUNK_SIZE_Y; y++) {
|
||||
for (int z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||
chunk->blocks[x][y][z] = (Block){ .type = 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Places a tree at the position specified by x, y, z
|
||||
|
||||
@ -3,8 +3,13 @@
|
||||
#include "raymath.h"
|
||||
#include "playerController.h"
|
||||
#include "blockTypes.h"
|
||||
#include "world.h"
|
||||
#include <math.h>
|
||||
|
||||
inline int FloorDiv(int a, int b) {
|
||||
return (a >= 0) ? (a / b) : ((a - b + 1) / b);
|
||||
}
|
||||
|
||||
void UpdatePlayer(Player *player) {
|
||||
Vector2 mouseDelta = GetMouseDelta();
|
||||
player->playerOrientation.x += mouseDelta.x * -0.002f;
|
||||
@ -45,68 +50,69 @@ void UpdatePlayer(Player *player) {
|
||||
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
|
||||
RaycastHit GetPlayerRaycastHit(Player *player, World *world, float maxDistance) {
|
||||
RaycastHit result = {0};
|
||||
Vector3 origin = Vector3Add(player->camera.position, Vector3Scale(player->forward, 0.001f));
|
||||
Vector3 dir = Vector3Normalize(player->forward);
|
||||
|
||||
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;
|
||||
int stepX = (dir.x > 0) ? 1 : -1;
|
||||
int stepY = (dir.y > 0) ? 1 : -1;
|
||||
int stepZ = (dir.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);
|
||||
float tMaxX = ((stepX > 0 ? (x + 1) : x) - origin.x) / dir.x;
|
||||
float tMaxY = ((stepY > 0 ? (y + 1) : y) - origin.y) / dir.y;
|
||||
float tMaxZ = ((stepZ > 0 ? (z + 1) : z) - origin.z) / dir.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 tDeltaX = fabsf(1.0f / dir.x);
|
||||
float tDeltaY = fabsf(1.0f / dir.y);
|
||||
float tDeltaZ = fabsf(1.0f / dir.z);
|
||||
|
||||
float t = 0.0f; // Total traveled distance along the ray
|
||||
Vector3 lastNormal = {0}; // Which face was entered (used for highlighting/interactions)
|
||||
float t = 0.0f;
|
||||
Vector3 lastNormal = {0};
|
||||
|
||||
// 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) {
|
||||
for (int i = 0; i < (int)(maxDistance * 3); i++) {
|
||||
int chunkX = FloorDiv(x, CHUNK_SIZE_X);
|
||||
int chunkZ = FloorDiv(z, CHUNK_SIZE_Z);
|
||||
Chunk *chunk = GetChunk(world, chunkX, chunkZ);
|
||||
|
||||
int blockID = chunk->blocks[x][y][z].type;
|
||||
if (chunk) {
|
||||
int localX = x - chunkX * CHUNK_SIZE_X;
|
||||
int localY = y;
|
||||
int localZ = z - chunkZ * CHUNK_SIZE_Z;
|
||||
|
||||
// If it's not air, we hit something!
|
||||
if (blockID != BLOCK_AIR) {
|
||||
if (localX >= 0 && localX < CHUNK_SIZE_X &&
|
||||
localY >= 0 && localY < CHUNK_SIZE_Y &&
|
||||
localZ >= 0 && localZ < CHUNK_SIZE_Z) {
|
||||
|
||||
Block block = chunk->blocks[localX][localY][localZ];
|
||||
if (block.type != BLOCK_AIR) {
|
||||
result.hit = true;
|
||||
result.blockID = blockID;
|
||||
result.position = (Vector3){x, y, z};
|
||||
result.hitBlockX = x;
|
||||
result.hitBlockY = y;
|
||||
result.hitBlockZ = z;
|
||||
result.normal = lastNormal;
|
||||
result.chunkX = chunkX;
|
||||
result.chunkZ = chunkZ;
|
||||
result.t = t;
|
||||
|
||||
// For visual debugging
|
||||
result.position = Vector3Add((Vector3){x, y, z}, Vector3Scale(lastNormal, 0.5f));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next voxel along the smallest tMax (i.e., the closest boundary)
|
||||
// DDA step
|
||||
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
|
||||
x += stepX;
|
||||
t = tMaxX;
|
||||
tMaxX += tDeltaX;
|
||||
lastNormal = (Vector3){-stepX, 0, 0}; // Normal points opposite the ray step
|
||||
lastNormal = (Vector3){-stepX, 0, 0};
|
||||
} else if (tMaxY < tMaxZ) {
|
||||
y += stepY;
|
||||
t = tMaxY;
|
||||
@ -118,35 +124,10 @@ RaycastHit RaycastChunk(const Chunk *chunk, Vector3 origin, Vector3 direction, f
|
||||
tMaxZ += tDeltaZ;
|
||||
lastNormal = (Vector3){0, 0, -stepZ};
|
||||
}
|
||||
|
||||
if (t > maxDistance) break;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,24 +9,19 @@
|
||||
#include "blockTypes.h"
|
||||
#include "playerController.h"
|
||||
#include "chunkGenerator.h"
|
||||
#include "chunkIO.h"
|
||||
#include "world.h"
|
||||
|
||||
int main(void) {
|
||||
// --- Screen setup ---
|
||||
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||
InitWindow(800, 600, "VoxelThing");
|
||||
InitWindow(800, 600, "voxelThing");
|
||||
SetExitKey(-1);
|
||||
DisableCursor();
|
||||
|
||||
// --- World generation ---
|
||||
Chunk chunk;
|
||||
if (!LoadChunk(&chunk, "saves/chunk_0_0_0.dat")) {
|
||||
// There was no save, gotta generate a fresh chunk to play with.
|
||||
printf("--- WORLDGEN--- No save, creating new chunk.\n");
|
||||
GenerateFlatChunk(&chunk);
|
||||
PlaceTreeAt(&chunk, 8, 64, 8);
|
||||
SaveChunk(&chunk, "saves/chunk_0_0_0.dat");
|
||||
}
|
||||
Mesh chunkMesh = GenerateChunkMesh(&chunk);
|
||||
World world;
|
||||
InitWorld(&world);
|
||||
|
||||
// --- Load textures and materials ---
|
||||
Texture2D atlas = LoadTexture("assets/TextureAtlas.png");
|
||||
@ -48,7 +43,7 @@ int main(void) {
|
||||
|
||||
bool paused = false;
|
||||
RaycastHit hit;
|
||||
int blockSelection = BLOCK_SAND;
|
||||
int blockSelection = BLOCK_STONE;
|
||||
|
||||
while (!WindowShouldClose()) {
|
||||
int screenWidth = GetScreenWidth();
|
||||
@ -78,12 +73,53 @@ int main(void) {
|
||||
|
||||
if (!paused) {
|
||||
UpdatePlayer(&player);
|
||||
hit = GetPlayerRaycastHit(&player, &chunk, 10.0f);
|
||||
HandleBlockInteraction(&player, &chunk, hit, blockSelection);
|
||||
hit = GetPlayerRaycastHit(&player, &world, 10.0f);
|
||||
|
||||
if (hit.hit && (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))) {
|
||||
UnloadMesh(chunkMesh);
|
||||
chunkMesh = GenerateChunkMesh(&chunk);
|
||||
|
||||
if (hit.hit) {
|
||||
int bx = hit.hitBlockX;
|
||||
int by = hit.hitBlockY;
|
||||
int bz = hit.hitBlockZ;
|
||||
|
||||
int placeX = bx + (int)hit.normal.x;
|
||||
int placeY = by + (int)hit.normal.y;
|
||||
int placeZ = bz + (int)hit.normal.z;
|
||||
|
||||
// Handle removal
|
||||
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||
Chunk *targetChunk = GetChunkContainingBlock(&world, bx, bz);
|
||||
if (targetChunk) {
|
||||
int lx = bx - targetChunk->x * CHUNK_SIZE_X;
|
||||
int lz = bz - targetChunk->z * CHUNK_SIZE_Z;
|
||||
|
||||
if (lx >= 0 && lx < CHUNK_SIZE_X && lz >= 0 && lz < CHUNK_SIZE_Z &&
|
||||
by >= 0 && by < CHUNK_SIZE_Y) {
|
||||
targetChunk->blocks[lx][by][lz].type = BLOCK_AIR;
|
||||
UnloadMesh(targetChunk->mesh);
|
||||
targetChunk->mesh = GenerateChunkMesh(targetChunk);
|
||||
// Mark chunk as changed.
|
||||
targetChunk->hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle placement
|
||||
if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
|
||||
Chunk *targetChunk = GetChunkContainingBlock(&world, placeX, placeZ);
|
||||
if (targetChunk) {
|
||||
int lx = placeX - targetChunk->x * CHUNK_SIZE_X;
|
||||
int lz = placeZ - targetChunk->z * CHUNK_SIZE_Z;
|
||||
|
||||
if (lx >= 0 && lx < CHUNK_SIZE_X && lz >= 0 && lz < CHUNK_SIZE_Z &&
|
||||
placeY >= 0 && placeY < CHUNK_SIZE_Y) {
|
||||
targetChunk->blocks[lx][placeY][lz].type = blockSelection;
|
||||
UnloadMesh(targetChunk->mesh);
|
||||
targetChunk->mesh = GenerateChunkMesh(targetChunk);
|
||||
// Mark chunk as changed.
|
||||
targetChunk->hasChanged = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,11 +127,21 @@ int main(void) {
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
BeginMode3D(player.camera);
|
||||
DrawMesh(chunkMesh, mat, MatrixIdentity());
|
||||
//DrawMesh(chunkMesh, mat, MatrixIdentity());
|
||||
for (int x = 0; x < WORLD_SIZE_X; x++) {
|
||||
for (int z = 0; z < WORLD_SIZE_Z; z++) {
|
||||
Chunk *chunk = world.chunks[x][z];
|
||||
if (chunk && chunk->mesh.vertexCount > 0) {
|
||||
DrawMesh(chunk->mesh, mat, MatrixTranslate(x * CHUNK_SIZE_X, 0, z * CHUNK_SIZE_Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hit.hit) {
|
||||
DrawCubeWires(Vector3Add(hit.position, (Vector3){0.5f, 0.5f, 0.5f}), 1.02f, 1.02f, 1.02f, BLACK);
|
||||
//DrawFaceHighlight(hit.position, hit.normal);
|
||||
Vector3 mid = Vector3Add((Vector3){hit.hitBlockX, hit.hitBlockY, hit.hitBlockZ},
|
||||
(Vector3){0.5f, 0.5f, 0.5f});
|
||||
DrawCubeWires(mid, 1.02f, 1.02f, 1.02f, BLACK);
|
||||
//DrawLine3D(mid, Vector3Add(mid, hit.normal), RED); // Normal direction
|
||||
}
|
||||
|
||||
EndMode3D();
|
||||
@ -107,6 +153,8 @@ int main(void) {
|
||||
// Debug info
|
||||
DrawText(TextFormat("Yaw: %.1f Pitch: %.1f", player.playerOrientation.x * RAD2DEG, player.playerOrientation.y * RAD2DEG), 10, 10, 20, DARKGRAY);
|
||||
|
||||
}
|
||||
|
||||
if (paused) {
|
||||
DrawRectangle(0, 0, screenWidth, screenHeight, Fade(DARKGRAY, 0.5f));
|
||||
DrawText("Paused", screenWidth / 2 - MeasureText("Paused", 40) / 2, screenHeight / 2 - 20, 40, RAYWHITE);
|
||||
@ -117,9 +165,8 @@ int main(void) {
|
||||
}
|
||||
|
||||
// --- Cleanup ---
|
||||
SaveChunk(&chunk, "saves/chunk_0_0_0.dat");
|
||||
UnloadTexture(atlas);
|
||||
UnloadMesh(chunkMesh);
|
||||
FreeWorld(&world);
|
||||
UnloadMaterial(mat);
|
||||
CloseWindow();
|
||||
return 0;
|
||||
|
||||
60
source/world.c
Normal file
60
source/world.c
Normal file
@ -0,0 +1,60 @@
|
||||
// world.c
|
||||
#include <stdlib.h>
|
||||
#include "world.h"
|
||||
#include "chunkGenerator.h"
|
||||
#include "chunkRenderer.h"
|
||||
#include "chunkStructures.h"
|
||||
#include "chunkIO.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void InitWorld(World *world) {
|
||||
for (int x = 0; x < WORLD_SIZE_X; x++) {
|
||||
for (int z = 0; z < WORLD_SIZE_Z; z++) {
|
||||
world->chunks[x][z] = malloc(sizeof(Chunk));
|
||||
Chunk *chunk = world->chunks[x][z];
|
||||
InitChunk(chunk, x, z);
|
||||
|
||||
if (!LoadChunk(chunk)) {
|
||||
// Later
|
||||
//GenerateChunkTerrain(chunk);
|
||||
GenerateFlatChunk(chunk);
|
||||
SaveChunk(chunk);
|
||||
}
|
||||
|
||||
chunk->mesh = GenerateChunkMesh(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeWorld(World *world) {
|
||||
for (int x = 0; x < WORLD_SIZE_X; x++) {
|
||||
for (int z = 0; z < WORLD_SIZE_Z; z++) {
|
||||
Chunk *chunk = world->chunks[x][z];
|
||||
if(chunk->hasChanged) SaveChunk(chunk);
|
||||
if (chunk) {
|
||||
UnloadMesh(chunk->mesh);
|
||||
free(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Chunk *GetChunkContainingBlock(World *world, int wx, int wz) {
|
||||
int chunkX = wx / CHUNK_SIZE_X;
|
||||
int chunkZ = wz / CHUNK_SIZE_Z;
|
||||
if (wx < 0) chunkX--;
|
||||
if (wz < 0) chunkZ--;
|
||||
return GetChunk(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
|
||||
void UpdateWorld(World *world) {
|
||||
// For now, stub function. Will handle streaming later.
|
||||
}
|
||||
|
||||
Chunk *GetChunk(World *world, int chunkX, int chunkZ) {
|
||||
if (chunkX >= 0 && chunkX < WORLD_SIZE_X && chunkZ >= 0 && chunkZ < WORLD_SIZE_Z) {
|
||||
return world->chunks[chunkX][chunkZ];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user