366 lines
13 KiB
C++
366 lines
13 KiB
C++
#include "Sky.hpp"
|
|
#include <rlgl.h>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
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);
|
|
} |