From 84df6120995a3a26be40b64a28f51d760ee08c04 Mon Sep 17 00:00:00 2001 From: dawidg81 Date: Fri, 13 Feb 2026 19:12:56 +0100 Subject: Updated sample4.cpp --- examples/sample4.cpp | 500 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 367 insertions(+), 133 deletions(-) diff --git a/examples/sample4.cpp b/examples/sample4.cpp index df9203e..6c9b7fc 100644 --- a/examples/sample4.cpp +++ b/examples/sample4.cpp @@ -1,16 +1,20 @@ #include "graphics.h" #include #include +#include #include #include struct Vec3 { float x, y, z; + + Vec3() : x(0), y(0), z(0) {} + Vec3(float x, float y, float z) : x(x), y(y), z(z) {} }; -Vec3 operator+(const Vec3& a, const Vec3& b) { return {a.x+b.x,a.y+b.y,a.z+b.z}; } -Vec3 operator-(const Vec3& a, const Vec3& b) { return {a.x-b.x,a.y-b.y,a.z-b.z}; } -Vec3 operator*(const Vec3& a, float s) { return {a.x*s,a.y*s,a.z*s}; } +Vec3 operator+(const Vec3& a, const Vec3& b) { return Vec3(a.x+b.x, a.y+b.y, a.z+b.z); } +Vec3 operator-(const Vec3& a, const Vec3& b) { return Vec3(a.x-b.x, a.y-b.y, a.z-b.z); } +Vec3 operator*(const Vec3& a, float s) { return Vec3(a.x*s, a.y*s, a.z*s); } float length(const Vec3& v) { return std::sqrt(v.x*v.x + v.y*v.y + v.z*v.z); @@ -18,87 +22,199 @@ float length(const Vec3& v) { Vec3 normalize(const Vec3& v) { float l = length(v); - if (l == 0) return {0,0,0}; - return {v.x/l, v.y/l, v.z/l}; + if (l == 0) return Vec3(0,0,0); + return Vec3(v.x/l, v.y/l, v.z/l); } Vec3 rotateX(const Vec3& v, float a) { float c = std::cos(a), s = std::sin(a); - return { v.x, v.y*c - v.z*s, v.y*s + v.z*c }; + return Vec3(v.x, v.y*c - v.z*s, v.y*s + v.z*c); } Vec3 rotateY(const Vec3& v, float a) { float c = std::cos(a), s = std::sin(a); - return { v.x*c + v.z*s, v.y, -v.x*s + v.z*c }; + return Vec3(v.x*c + v.z*s, v.y, -v.x*s + v.z*c); } -// Camera +// Smooth camera with interpolation struct Camera { Vec3 position; + Vec3 velocity; float yaw; float pitch; + float yawVelocity; + float pitchVelocity; }; -void project(const Vec3& v, int& x2d, int& y2d, - int width, int height, float fov) -{ - if (v.z <= 0.1f) return; - +// Better projection with near plane clipping +bool project(const Vec3& v, int& x2d, int& y2d, int width, int height, float fov) { + const float nearPlane = 0.5f; // Prevent rendering too close objects + + if (v.z <= nearPlane) return false; + float factor = fov / v.z; x2d = static_cast(v.x * factor + width/2); y2d = static_cast(v.y * factor + height/2); + + // Check if within screen bounds (with margin) + return (x2d >= -100 && x2d < width + 100 && y2d >= -100 && y2d < height + 100); } struct Star { Vec3 pos; + uint8_t brightness; }; struct Cube { Vec3 pos; float size; + Color color; }; struct Planet { Vec3 pos; float radius; + Color color; +}; + +// Chunk-based world system for infinite generation +struct ChunkCoord { + int x, y, z; + + bool operator<(const ChunkCoord& other) const { + if (x != other.x) return x < other.x; + if (y != other.y) return y < other.y; + return z < other.z; + } +}; + +struct Chunk { + std::vector stars; + std::vector cubes; + std::vector planets; }; +// Deterministic random based on chunk coordinates +unsigned int hashCoord(int x, int y, int z, int seed) { + unsigned int h = seed; + h ^= x * 374761393; + h ^= y * 668265263; + h ^= z * 1274126177; + h = (h ^ (h >> 16)) * 0x85ebca6b; + h = (h ^ (h >> 13)) * 0xc2b2ae35; + h = h ^ (h >> 16); + return h; +} + +float randomFloat(unsigned int& seed) { + seed = seed * 1103515245 + 12345; + return ((seed / 65536) % 32768) / 32768.0f; +} + +Chunk generateChunk(const ChunkCoord& coord) { + Chunk chunk; + + unsigned int seed = hashCoord(coord.x, coord.y, coord.z, 12345); + + const float chunkSize = 100.0f; + Vec3 chunkOrigin(coord.x * chunkSize, coord.y * chunkSize, coord.z * chunkSize); + + // Generate stars (lots of them) + int starCount = 80 + (hashCoord(coord.x, coord.y, coord.z, 1) % 40); + for (int i = 0; i < starCount; ++i) { + Star star; + star.pos = Vec3( + chunkOrigin.x + randomFloat(seed) * chunkSize, + chunkOrigin.y + randomFloat(seed) * chunkSize, + chunkOrigin.z + randomFloat(seed) * chunkSize + ); + star.brightness = 128 + static_cast(randomFloat(seed) * 127); + chunk.stars.push_back(star); + } + + // Generate cubes (fewer) + int cubeCount = 2 + (hashCoord(coord.x, coord.y, coord.z, 2) % 4); + for (int i = 0; i < cubeCount; ++i) { + Cube cube; + cube.pos = Vec3( + chunkOrigin.x + randomFloat(seed) * chunkSize, + chunkOrigin.y + randomFloat(seed) * chunkSize, + chunkOrigin.z + randomFloat(seed) * chunkSize + ); + cube.size = 1.0f + randomFloat(seed) * 4.0f; + + // Varied colors + uint8_t r = static_cast(randomFloat(seed) * 100 + 155); + uint8_t g = static_cast(randomFloat(seed) * 100 + 155); + uint8_t b = static_cast(randomFloat(seed) * 100 + 155); + cube.color = Color(r, g, b); + + chunk.cubes.push_back(cube); + } + + // Generate planets (rare) + if (randomFloat(seed) < 0.3f) { + Planet planet; + planet.pos = Vec3( + chunkOrigin.x + randomFloat(seed) * chunkSize, + chunkOrigin.y + randomFloat(seed) * chunkSize, + chunkOrigin.z + randomFloat(seed) * chunkSize + ); + planet.radius = 5.0f + randomFloat(seed) * 15.0f; + + uint8_t r = static_cast(randomFloat(seed) * 150 + 105); + uint8_t g = static_cast(randomFloat(seed) * 150 + 105); + uint8_t b = static_cast(randomFloat(seed) * 150 + 105); + planet.color = Color(r, g, b); + + chunk.planets.push_back(planet); + } + + return chunk; +} + +ChunkCoord worldToChunk(const Vec3& pos) { + const float chunkSize = 100.0f; + return ChunkCoord{ + static_cast(std::floor(pos.x / chunkSize)), + static_cast(std::floor(pos.y / chunkSize)), + static_cast(std::floor(pos.z / chunkSize)) + }; +} + int main() { std::srand(static_cast(std::time(nullptr))); - const int width = 1280; - const int height = 720; + const int width = 1920; + const int height = 1080; - WindowHandle* window = createWindow("SAMPLE4 - SPACE NAVIGATION", width, height); + WindowHandle* window = createWindow("SAMPLE4 - INFINITE SPACE NAVIGATION", width, height); if (!window) return -1; setMouseLocked(window, true); Camera cam; - cam.position = {0,0,-5}; + cam.position = Vec3(0, 0, -5); + cam.velocity = Vec3(0, 0, 0); cam.yaw = 0; cam.pitch = 0; - - const float moveSpeed = 0.3f; - const float mouseSensitivity = 0.002f; - - // ----- STARFIELD ----- - const int starCount = 1500; - std::vector stars; - for (int i = 0; i < starCount; ++i) { - stars.push_back({ - { - (rand()%2000 - 1000) / 10.0f, - (rand()%2000 - 1000) / 10.0f, - (rand()%2000 - 1000) / 10.0f - } - }); - } - - // ----- CUBES ----- + cam.yawVelocity = 0; + cam.pitchVelocity = 0; + + const float moveAccel = 0.01f; // 0.8f + const float moveDamping = 1.0f; // 0.88f + const float maxSpeed = 10.0f; // 1.5f + const float mouseSensitivity = 0.003f; // 0.003f + const float mouseSmoothing = 0.3f; // 0.3f + + const float viewDistance = 300.0f; // 300.0f + const int renderRadius = 1; // Chunks to render in each direction; default = 3 + float speed = 0.0f; + + // Cube vertices (shared) std::vector cubeVertices = { - {-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}, - {-1,-1,1},{1,-1,1},{1,1,1},{-1,1,1} + Vec3(-1,-1,-1), Vec3(1,-1,-1), Vec3(1,1,-1), Vec3(-1,1,-1), + Vec3(-1,-1,1), Vec3(1,-1,1), Vec3(1,1,1), Vec3(-1,1,1) }; std::vector> cubeEdges = { @@ -107,135 +223,253 @@ int main() { {0,4},{1,5},{2,6},{3,7} }; - std::vector cubes; - for (int i = 0; i < 40; ++i) { - cubes.push_back({ - { - (rand()%2000 - 1000) / 10.0f, - (rand()%2000 - 1000) / 10.0f, - (rand()%2000 - 1000) / 10.0f - }, - (rand()%50)/10.0f + 1.0f - }); - } - - // ----- PLANETS ----- - std::vector planets; - for (int i = 0; i < 5; ++i) { - planets.push_back({ - { - (rand()%3000 - 1500) / 10.0f, - (rand()%3000 - 1500) / 10.0f, - (rand()%3000 - 1500) / 10.0f - }, - (rand()%200)/10.0f + 5.0f - }); - } + // Chunk cache + std::map chunkCache; + + // FPS counter + int frameCount = 0; + float fpsTimer = 0; + int fps = 0; while (!windowShouldClose(window)) { - pollEvents(window); - // ----- MOUSE LOOK ----- + // ----- SMOOTH MOUSE LOOK ----- int dx, dy; getMouseDelta(window, dx, dy); - cam.yaw += dx * mouseSensitivity; - cam.pitch += dy * mouseSensitivity; - - if (cam.pitch > 1.5f) cam.pitch = 1.5f; - if (cam.pitch < -1.5f) cam.pitch = -1.5f; - - // Forward vector - Vec3 forward = { - std::sin(cam.yaw), - 0, - std::cos(cam.yaw) - }; - - Vec3 right = { + cam.yawVelocity = cam.yawVelocity * (1.0f - mouseSmoothing) + dx * mouseSensitivity * mouseSmoothing; + cam.pitchVelocity = cam.pitchVelocity * (1.0f - mouseSmoothing) + -dy * mouseSensitivity * mouseSmoothing; + + cam.yaw += cam.yawVelocity; + cam.pitch += cam.pitchVelocity; + + // Clamp pitch + const float maxPitch = 1.5f; + if (cam.pitch > maxPitch) cam.pitch = maxPitch; + if (cam.pitch < -maxPitch) cam.pitch = -maxPitch; + + // Direction vectors + Vec3 forward( + std::sin(cam.yaw) * std::cos(cam.pitch), + -std::sin(cam.pitch), + std::cos(cam.yaw) * std::cos(cam.pitch) + ); + + Vec3 right( std::cos(cam.yaw), 0, -std::sin(cam.yaw) - }; + ); + + Vec3 up(0, 1, 0); + + // ----- SMOOTH MOVEMENT ----- + Vec3 inputVelocity(0, 0, 0); + + if (keyDown(window, KEY_W) || keyDown(window, KEY_UP)) + inputVelocity = inputVelocity + forward; + if (keyDown(window, KEY_S) || keyDown(window, KEY_DOWN)) + inputVelocity = inputVelocity - forward; + if (keyDown(window, KEY_A) || keyDown(window, KEY_LEFT)) + inputVelocity = inputVelocity - right; + if (keyDown(window, KEY_D) || keyDown(window, KEY_RIGHT)) + inputVelocity = inputVelocity + right; + if (keyDown(window, KEY_E)) + inputVelocity = inputVelocity + up; + if (keyDown(window, KEY_Q)) + inputVelocity = inputVelocity - up; + + // Normalize input to prevent faster diagonal movement + float inputLen = length(inputVelocity); + if (inputLen > 0.01f) { + inputVelocity = normalize(inputVelocity) * moveAccel; + } - // ----- MOVEMENT ----- - if (keyDown(window, KEY_UP)) - cam.position = cam.position + forward * moveSpeed; - if (keyDown(window, KEY_DOWN)) - cam.position = cam.position - forward * moveSpeed; - if (keyDown(window, KEY_LEFT)) - cam.position = cam.position - right * moveSpeed; - if (keyDown(window, KEY_RIGHT)) - cam.position = cam.position + right * moveSpeed; + // Apply acceleration + cam.velocity = cam.velocity + inputVelocity; + + // Apply damping + cam.velocity = cam.velocity * moveDamping; + + // Clamp to max speed + speed = length(cam.velocity); + if (speed > maxSpeed) { + cam.velocity = normalize(cam.velocity) * maxSpeed; + } + + // Update position + cam.position = cam.position + cam.velocity; + + // ----- CHUNK LOADING/UNLOADING ----- + ChunkCoord currentChunk = worldToChunk(cam.position); + + // Load nearby chunks + for (int x = -renderRadius; x <= renderRadius; ++x) { + for (int y = -renderRadius; y <= renderRadius; ++y) { + for (int z = -renderRadius; z <= renderRadius; ++z) { + ChunkCoord coord{currentChunk.x + x, currentChunk.y + y, currentChunk.z + z}; + + if (chunkCache.find(coord) == chunkCache.end()) { + chunkCache[coord] = generateChunk(coord); + } + } + } + } + + // Unload distant chunks (keep cache from growing too large) + std::vector toRemove; + for (auto& pair : chunkCache) { + int dx = pair.first.x - currentChunk.x; + int dy = pair.first.y - currentChunk.y; + int dz = pair.first.z - currentChunk.z; + + if (std::abs(dx) > renderRadius + 1 || + std::abs(dy) > renderRadius + 1 || + std::abs(dz) > renderRadius + 1) { + toRemove.push_back(pair.first); + } + } + + for (auto& coord : toRemove) { + chunkCache.erase(coord); + } - clearScreen(window, Color(0,0,10)); + // ----- RENDERING ----- + clearScreen(window, Color(0, 0, 5)); - float fov = 500.0f; + const float fov = 600.0f; + int starsRendered = 0; + int cubesRendered = 0; + int planetsRendered = 0; // ----- DRAW STARS ----- - for (auto& star : stars) { - - Vec3 p = star.pos - cam.position; - p = rotateY(p, -cam.yaw); - p = rotateX(p, -cam.pitch); + for (auto& chunkPair : chunkCache) { + for (auto& star : chunkPair.second.stars) { + Vec3 p = star.pos - cam.position; + + // Distance culling + float dist = length(p); + if (dist > viewDistance) continue; + + p = rotateY(p, -cam.yaw); + p = rotateX(p, -cam.pitch); - if (p.z > 0) { int sx, sy; - project(p, sx, sy, width, height, fov); - if (sx>=0 && sx=0 && sy= 0 && sx < width && sy >= 0 && sy < height) { + // Dimmer stars when far away + float brightness = 1.0f - (dist / viewDistance); + uint8_t b = static_cast(star.brightness * brightness); + drawPixel(window, sx, sy, Color(b, b, b)); + starsRendered++; + } + } } } // ----- DRAW CUBES ----- - for (auto& cube : cubes) { - - std::vector transformed; + for (auto& chunkPair : chunkCache) { + for (auto& cube : chunkPair.second.cubes) { + Vec3 cubePos = cube.pos - cam.position; + + // Distance culling + if (length(cubePos) > viewDistance) continue; + + std::vector transformed; + bool anyVisible = false; + + for (auto v : cubeVertices) { + v = v * cube.size; + v = v + cube.pos; + v = v - cam.position; + v = rotateY(v, -cam.yaw); + v = rotateX(v, -cam.pitch); + transformed.push_back(v); + + if (v.z > -1.0f) anyVisible = true; // 0.5 + } - for (auto v : cubeVertices) { - v = v * cube.size; - v = v + cube.pos; - v = v - cam.position; - v = rotateY(v, -cam.yaw); - v = rotateX(v, -cam.pitch); - transformed.push_back(v); + // Only render if at least one vertex is visible + if (!anyVisible) continue; + + for (auto& e : cubeEdges) { + // Only draw edge if both vertices are in front of camera + if (transformed[e.first].z > 0.5f && transformed[e.second].z > 0.5f) { + int x1, y1, x2, y2; + if (project(transformed[e.first], x1, y1, width, height, fov) && + project(transformed[e.second], x2, y2, width, height, fov)) { + drawLine(window, x1, y1, x2, y2, cube.color); + } + } + } + cubesRendered++; } + } - for (auto& e : cubeEdges) { - if (transformed[e.first].z > 0 && - transformed[e.second].z > 0) { - - int x1,y1,x2,y2; - project(transformed[e.first], x1,y1,width,height,fov); - project(transformed[e.second],x2,y2,width,height,fov); - drawLine(window,x1,y1,x2,y2,Color(0,255,200)); + // ----- DRAW PLANETS ----- + for (auto& chunkPair : chunkCache) { + for (auto& planet : chunkPair.second.planets) { + Vec3 p = planet.pos - cam.position; + + // Distance culling + if (length(p) > viewDistance) continue; + + p = rotateY(p, -cam.yaw); + p = rotateX(p, -cam.pitch); + + int cx, cy; + if (project(p, cx, cy, width, height, fov)) { + float scale = fov / p.z; + int radius2D = static_cast(planet.radius * scale); + + // Only draw if reasonable size + if (radius2D > 1 && radius2D < 500) { + drawCircle(window, cx, cy, radius2D, planet.color); + planetsRendered++; + } } } } - // ----- DRAW PLANETS (wireframe sphere imitation) ----- - for (auto& planet : planets) { - - Vec3 p = planet.pos - cam.position; - p = rotateY(p, -cam.yaw); - p = rotateX(p, -cam.pitch); - - if (p.z <= 0) continue; - - int cx, cy; - project(p, cx, cy, width, height, fov); - - float scale = fov / p.z; - int radius2D = static_cast(planet.radius * scale); - - drawCircle(window, cx, cy, radius2D, Color(100,100,255)); + // ----- DEBUG INFO ----- + // Position text (top-left) + char posText[128]; + snprintf(posText, sizeof(posText), "X:%.1f Y:%.1f Z:%.1f", + cam.position.x, cam.position.y, cam.position.z); + + // Draw simple text using rectangles + int textY = 10; + for (int i = 0; posText[i] != '\0'; ++i) { + drawFilledRectangle(window, 10 + i * 8, textY, 6, 10, Color(0, 255, 0, 128)); + } + + // Render stats + snprintf(posText, sizeof(posText), "Stars:%d Cubes:%d Planets:%d Chunks:%d", + starsRendered, cubesRendered, planetsRendered, (int)chunkCache.size()); + textY = 25; + for (int i = 0; posText[i] != '\0'; ++i) { + drawFilledRectangle(window, 10 + i * 8, textY, 6, 10, Color(0, 200, 200, 128)); + } + + textY = 40; + for (int i = 0; speed * 3 > i; i++) { + drawFilledRectangle(window, 10 + i * 8, textY, 6, 10, Color(0, 200, 200, 128)); } swapBuffers(window); delay(16); + + // FPS counter + frameCount++; + fpsTimer += 16; + if (fpsTimer >= 1000) { + fps = frameCount; + frameCount = 0; + fpsTimer = 0; + } } destroyWindow(window); return 0; -} +} \ No newline at end of file -- cgit v1.2.3