diff --git a/client/sky/Sky.cpp b/client/sky/Sky.cpp new file mode 100644 index 0000000..90fffba --- /dev/null +++ b/client/sky/Sky.cpp @@ -0,0 +1,248 @@ +#include "Sky.hpp" +#include +#include +#include + +Sky::Sky() + : fogDensity(0.01f) + , timeOfDay(0.25f) // Start at sunrise + , shaderLoaded(false) { + + sun.intensity = 1.0f; + sun.angle = 0.0f; + + createSkyDome(); + updateColors(); + + // Try to load a custom shader if available + // For now we'll use basic rendering +} + +Sky::~Sky() { + if (shaderLoaded) { + UnloadShader(skyShader); + } + UnloadModel(skyModel); +} + +void Sky::createSkyDome() { + // Create a large inverted sphere for the sky dome + // The sphere is inverted so we see the inside surface + skyDome = GenMeshSphere(1000.0f, 32, 32); + + // Invert the mesh normals to render the inside + for (int i = 0; i < skyDome.vertexCount * 3; i++) { + skyDome.normals[i] *= -1.0f; + } + + // Update mesh in GPU + UploadMesh(&skyDome, false); + skyModel = LoadModelFromMesh(skyDome); +} + +void Sky::update(float) { + // Optional: auto-advance time of day for testing + // timeOfDay += deltaTime * 0.01f; // Very slow day cycle + // if (timeOfDay > 1.0f) timeOfDay -= 1.0f; + + updateColors(); + sun.updatePosition(timeOfDay); +} + +void Sky::Sun::updatePosition(float timeOfDay) { + // Convert time of day to angle (0.25 = sunrise at 6am, 0.75 = sunset at 6pm) + // Adjust so that 0.25 (6am) = 0 degrees, 0.75 (6pm) = 180 degrees + float adjustedTime = timeOfDay - 0.25f; + if (adjustedTime < 0) adjustedTime += 1.0f; + + // Only show sun during daytime (6am to 6pm) + if (timeOfDay >= 0.25f && timeOfDay <= 0.75f) { + float dayProgress = (timeOfDay - 0.25f) / 0.5f; // 0 to 1 during day + angle = dayProgress * PI; // 0 to PI (sunrise to sunset) + + // Calculate sun position in an arc + float sunX = cosf(angle) * 150.0f; // East to West + float sunY = sinf(angle) * 100.0f + 20.0f; // Height arc, minimum 20 units up + float sunZ = 0.0f; + + position = {sunX, sunY, sunZ}; + } else { + // Sun is below horizon at night + position = {0.0f, -100.0f, 0.0f}; + } + + // Direction is from sun to origin (for lighting) + direction = Vector3Normalize(Vector3Scale(position, -1.0f)); +} + +void Sky::updateColors() { + float t = timeOfDay; + + // Define key times + const float nightEnd = 0.2f; // 4:48 AM + const float sunrise = 0.25f; // 6:00 AM + const float noon = 0.5f; // 12:00 PM + const float sunset = 0.75f; // 6:00 PM + const float nightStart = 0.8f; // 7:12 PM + + Color nightHorizon = {10, 15, 30, 255}; + Color nightZenith = {5, 10, 20, 255}; + Color sunriseHorizon = {255, 150, 100, 255}; + Color sunriseZenith = {100, 150, 200, 255}; + Color dayHorizon = {150, 200, 255, 255}; + Color dayZenith = {100, 150, 255, 255}; + Color sunsetHorizon = {255, 100, 50, 255}; + Color sunsetZenith = {100, 100, 150, 255}; + + if (t < nightEnd) { + // Night + horizonColor = nightHorizon; + zenithColor = nightZenith; + sun.color = {50, 50, 70, 255}; + sun.intensity = 0.1f; + } else if (t < sunrise) { + // Night to sunrise transition + float blend = (t - nightEnd) / (sunrise - nightEnd); + horizonColor = interpolateColor(nightHorizon, sunriseHorizon, blend); + zenithColor = interpolateColor(nightZenith, sunriseZenith, blend); + sun.color = interpolateColor({50, 50, 70, 255}, {255, 200, 150, 255}, blend); + sun.intensity = 0.1f + blend * 0.4f; + } else if (t < noon) { + // Sunrise to noon + float blend = (t - sunrise) / (noon - sunrise); + horizonColor = interpolateColor(sunriseHorizon, dayHorizon, blend); + zenithColor = interpolateColor(sunriseZenith, dayZenith, blend); + sun.color = interpolateColor({255, 200, 150, 255}, {255, 255, 250, 255}, blend); + sun.intensity = 0.5f + blend * 0.5f; + } else if (t < sunset) { + // Noon to sunset + float blend = (t - noon) / (sunset - noon); + horizonColor = interpolateColor(dayHorizon, sunsetHorizon, blend); + zenithColor = interpolateColor(dayZenith, sunsetZenith, blend); + sun.color = interpolateColor({255, 255, 250, 255}, {255, 180, 100, 255}, blend); + sun.intensity = 1.0f - blend * 0.3f; + } else if (t < nightStart) { + // Sunset to night transition + float blend = (t - sunset) / (nightStart - sunset); + horizonColor = interpolateColor(sunsetHorizon, nightHorizon, blend); + zenithColor = interpolateColor(sunsetZenith, nightZenith, blend); + sun.color = interpolateColor({255, 180, 100, 255}, {50, 50, 70, 255}, blend); + sun.intensity = 0.7f - blend * 0.6f; + } else { + // Night + horizonColor = nightHorizon; + zenithColor = nightZenith; + sun.color = {50, 50, 70, 255}; + sun.intensity = 0.1f; + } + + // Fog color follows horizon color but slightly desaturated + fogColor = interpolateColor(horizonColor, {200, 200, 200, 255}, 0.3f); +} + +Color Sky::interpolateColor(const Color& a, const Color& b, float t) const { + t = std::clamp(t, 0.0f, 1.0f); + return { + (unsigned char)(a.r + (b.r - a.r) * t), + (unsigned char)(a.g + (b.g - a.g) * t), + (unsigned char)(a.b + (b.b - a.b) * t), + (unsigned char)(a.a + (b.a - a.a) * t) + }; +} + +void Sky::render(const Camera3D& camera) { + // Legacy function - just calls renderSkybox + renderSkybox(camera); +} + +void Sky::renderSkybox(const Camera3D& camera) { + // Save current depth state + rlPushMatrix(); + + // Disable depth write but keep depth test for proper rendering order + rlDisableDepthMask(); + + // Position skybox at camera position so it moves with the camera + // This ensures the skybox is always centered on the viewer + rlTranslatef(camera.position.x, camera.position.y, camera.position.z); + + // Draw the sky dome with gradient color + // We'll use vertex colors or a simple colored material + Color topColor = zenithColor; + Color bottomColor = horizonColor; + + // For now, draw with a blended color + Color skyColor = interpolateColor(bottomColor, topColor, 0.5f); + DrawModel(skyModel, {0, 0, 0}, 1.0f, skyColor); + + // Draw sun as a sphere on the skybox + if (sun.intensity > 0.2f && sun.position.y > 0) { + // Calculate sun direction from camera + Vector3 sunDir = Vector3Normalize(sun.position); + Vector3 sunPosOnSphere = Vector3Scale(sunDir, 900.0f); // Just inside the skybox + + // Draw sun as a sphere + DrawSphere(sunPosOnSphere, 50.0f, sun.color); + + // Draw sun glow layers + Color glowColor = sun.color; + glowColor.a = 80; + DrawSphere(sunPosOnSphere, 70.0f, glowColor); + + glowColor.a = 40; + DrawSphere(sunPosOnSphere, 90.0f, glowColor); + } + + // Restore depth state + rlEnableDepthMask(); + rlPopMatrix(); +} + +void Sky::drawGradientSky() const { + // Draw a simple gradient from horizon to zenith + int screenHeight = GetScreenHeight(); + int screenWidth = GetScreenWidth(); + + // Draw gradient rectangles + for (int y = 0; y < screenHeight; y++) { + float t = (float)y / (float)screenHeight; + Color gradientColor = interpolateColor(zenithColor, horizonColor, t); + DrawRectangle(0, y, screenWidth, 1, gradientColor); + } +} + +void Sky::drawSun(const Camera3D& camera) const { + // Only draw sun during day time + if (sun.intensity > 0.2f) { + // Calculate sun screen position + Vector2 sunScreenPos = GetWorldToScreen(sun.position, camera); + + // Check if sun is in view + if (sunScreenPos.x >= 0 && sunScreenPos.x <= GetScreenWidth() && + sunScreenPos.y >= 0 && sunScreenPos.y <= GetScreenHeight()) { + + // Draw sun glow + float glowRadius = 60.0f * sun.intensity; + Color glowColor = sun.color; + glowColor.a = 50; + DrawCircleGradient(sunScreenPos.x, sunScreenPos.y, glowRadius, + glowColor, {glowColor.r, glowColor.g, glowColor.b, 0}); + + // Draw sun disc + float sunRadius = 20.0f; + DrawCircle(sunScreenPos.x, sunScreenPos.y, sunRadius, sun.color); + } + } +} + +void Sky::setTimeOfDay(float time) { + timeOfDay = std::clamp(time, 0.0f, 1.0f); + updateColors(); + sun.updatePosition(timeOfDay); +} + +Color Sky::getAmbientLight() const { + // Ambient light based on time of day + float ambientIntensity = 0.2f + sun.intensity * 0.3f; + return interpolateColor({20, 20, 30, 255}, zenithColor, ambientIntensity); +} \ No newline at end of file diff --git a/client/sky/Sky.hpp b/client/sky/Sky.hpp new file mode 100644 index 0000000..6406fb6 --- /dev/null +++ b/client/sky/Sky.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +class Sky { +private: + struct Sun { + Vector3 position; + Vector3 direction; + Color color; + float intensity; + float angle; // Sun angle for day/night cycle + + void updatePosition(float timeOfDay); + Vector3 getLightDirection() const { return direction; } + }; + + Sun sun; + Color horizonColor; + Color zenithColor; + Color fogColor; + float fogDensity; + float timeOfDay; // 0.0 to 1.0 (0 = midnight, 0.5 = noon) + + Shader skyShader; + Mesh skyDome; + Model skyModel; + bool shaderLoaded; + +public: + Sky(); + ~Sky(); + + void update(float deltaTime); + void render(const Camera3D& camera); + void renderSkybox(const Camera3D& camera); // Render skybox that follows camera + + void setTimeOfDay(float time); + float getTimeOfDay() const { return timeOfDay; } + + Vector3 getSunDirection() const { return sun.direction; } + Color getSunColor() const { return sun.color; } + float getSunIntensity() const { return sun.intensity; } + + Color getFogColor() const { return fogColor; } + float getFogDensity() const { return fogDensity; } + + Color getAmbientLight() const; + +private: + void updateColors(); + void createSkyDome(); + Color interpolateColor(const Color& a, const Color& b, float t) const; + void drawGradientSky() const; + void drawSun(const Camera3D& camera) const; +}; \ No newline at end of file