#include "Sky.hpp" #include #include #include #include Sky::Sky() : fogDensity(0.01f) , timeOfDay(0.25f) // Start at sunrise , shaderLoaded(false) { sun.intensity = 1.0f; sun.angle = 0.0f; moon.intensity = 1.0f; moon.phase = 0.5f; // Start with full moon moon.color = {230, 230, 255, 255}; // Pale blue-white createSkyDome(); updateColors(); // Try to load the sky shader const char* vsPath = "shaders/sky.vs"; const char* fsPath = "shaders/sky.fs"; if (FileExists(vsPath) && FileExists(fsPath)) { skyShader = LoadShader(vsPath, fsPath); // Get shader uniform locations horizonColorLoc = GetShaderLocation(skyShader, "horizonColor"); zenithColorLoc = GetShaderLocation(skyShader, "zenithColor"); sunDirectionLoc = GetShaderLocation(skyShader, "sunDirection"); sunColorLoc = GetShaderLocation(skyShader, "sunColor"); sunIntensityLoc = GetShaderLocation(skyShader, "sunIntensity"); moonDirectionLoc = GetShaderLocation(skyShader, "moonDirection"); moonColorLoc = GetShaderLocation(skyShader, "moonColor"); moonIntensityLoc = GetShaderLocation(skyShader, "moonIntensity"); moonPhaseLoc = GetShaderLocation(skyShader, "moonPhase"); starsIntensityLoc = GetShaderLocation(skyShader, "starsIntensity"); timeLoc = GetShaderLocation(skyShader, "time"); // Assign shader to sky model skyModel.materials[0].shader = skyShader; shaderLoaded = true; std::cout << "Sky shader loaded successfully\n"; } else { std::cout << "Sky shader files not found, using fallback rendering\n"; } } Sky::~Sky() { if (shaderLoaded) { UnloadShader(skyShader); } UnloadModel(skyModel); } void Sky::createSkyDome() { // Create a large sphere for the sky dome skyDome = GenMeshSphere(500.0f, 16, 16); // Don't invert normals - let's use backface culling instead // Just upload the mesh as-is 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); moon.updatePosition(timeOfDay); } void Sky::updateFromServerTime(float serverTimeOfDay) { timeOfDay = serverTimeOfDay; updateColors(); sun.updatePosition(timeOfDay); moon.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::Moon::updatePosition(float timeOfDay) { // Moon follows opposite schedule to sun // Visible from 6 PM (0.75) to 6 AM (0.25) // At midnight (0.0), moon is overhead float adjustedTime = timeOfDay + 0.25f; // Shift so moon is overhead at midnight if (adjustedTime > 1.0f) adjustedTime -= 1.0f; // Only show moon during nighttime (6pm to 6am) if (timeOfDay < 0.25f || timeOfDay > 0.75f) { float nightProgress; if (timeOfDay > 0.75f) { nightProgress = (timeOfDay - 0.75f) / 0.5f; // 0 to 0.5 for evening } else { nightProgress = (timeOfDay + 0.25f) / 0.5f; // 0.5 to 1 for morning } float angle = nightProgress * PI; // 0 to PI (moonrise to moonset) // Calculate moon position in an arc (same as sun but at night) float moonX = cosf(angle) * 150.0f; // East to West float moonY = sinf(angle) * 100.0f + 20.0f; // Height arc, minimum 20 units up float moonZ = 0.0f; position = {moonX, moonY, moonZ}; } else { // Moon is below horizon during day position = {0.0f, -100.0f, 0.0f}; } // Direction is from moon to origin 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(); // Disable backface culling so we can see the inside of the sphere rlDisableBackfaceCulling(); // 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); if (shaderLoaded) { // First make sure sun and moon positions are updated sun.updatePosition(timeOfDay); moon.updatePosition(timeOfDay); // Update shader uniforms Vector3 horizonColorVec = { horizonColor.r / 255.0f, horizonColor.g / 255.0f, horizonColor.b / 255.0f }; Vector3 zenithColorVec = { zenithColor.r / 255.0f, zenithColor.g / 255.0f, zenithColor.b / 255.0f }; Vector3 sunColorVec = { sun.color.r / 255.0f, sun.color.g / 255.0f, sun.color.b / 255.0f }; Vector3 moonColorVec = { moon.color.r / 255.0f, moon.color.g / 255.0f, moon.color.b / 255.0f }; // Normalize sun direction (make sure it's valid) Vector3 sunDir = {0, 1, 0}; // Default up if sun position is invalid if (Vector3Length(sun.position) > 0.001f) { sunDir = Vector3Normalize(sun.position); } // Normalize moon direction Vector3 moonDir = {0, -1, 0}; // Default down if moon position is invalid if (Vector3Length(moon.position) > 0.001f) { moonDir = Vector3Normalize(moon.position); } // Calculate stars intensity based on time of day (visible at night) float starsIntensity = 0.0f; if (timeOfDay < 0.2f || timeOfDay > 0.8f) { // Full intensity at midnight if (timeOfDay > 0.5f) { starsIntensity = (timeOfDay - 0.8f) / 0.2f; } else { starsIntensity = 1.0f - (timeOfDay / 0.2f); } } // Set shader uniforms SetShaderValue(skyShader, horizonColorLoc, &horizonColorVec, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, zenithColorLoc, &zenithColorVec, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, sunDirectionLoc, &sunDir, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, sunColorLoc, &sunColorVec, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, sunIntensityLoc, &sun.intensity, SHADER_UNIFORM_FLOAT); SetShaderValue(skyShader, moonDirectionLoc, &moonDir, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, moonColorLoc, &moonColorVec, SHADER_UNIFORM_VEC3); SetShaderValue(skyShader, moonIntensityLoc, &moon.intensity, SHADER_UNIFORM_FLOAT); SetShaderValue(skyShader, moonPhaseLoc, &moon.phase, SHADER_UNIFORM_FLOAT); SetShaderValue(skyShader, starsIntensityLoc, &starsIntensity, SHADER_UNIFORM_FLOAT); // Pass current time for cloud animation float currentTime = GetTime(); SetShaderValue(skyShader, timeLoc, ¤tTime, SHADER_UNIFORM_FLOAT); // Draw the sky dome with shader DrawModel(skyModel, {0, 0, 0}, 1.0f, WHITE); } else { // Fallback: Draw without shader Color skyColor = interpolateColor(horizonColor, zenithColor, 0.5f); DrawModel(skyModel, {0, 0, 0}, 1.0f, skyColor); } // Restore depth state rlEnableBackfaceCulling(); 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); }