From 6ff828a167f872d566d014880b701b79a99633ea Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Tue, 9 Sep 2025 14:29:46 -0500 Subject: [PATCH] yay procedural sky! --- client/main.cpp | 20 ++++---- client/shaders/sky.fs | 65 ++++++++++++++++++++++++++ client/shaders/sky.vs | 34 ++++++++++++++ client/sky/Sky.cpp | 103 ++++++++++++++++++++++++++++-------------- client/sky/Sky.hpp | 24 ++++++---- 5 files changed, 193 insertions(+), 53 deletions(-) create mode 100644 client/shaders/sky.fs create mode 100644 client/shaders/sky.vs diff --git a/client/main.cpp b/client/main.cpp index 028c31e..b70fd58 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -126,14 +126,14 @@ class Game { bool editMode = false; std::string loginError = ""; std::string currentUsername = ""; - + // Heartbeat timing float lastHeartbeatTime = 0.0f; const float HEARTBEAT_INTERVAL = 5.0f; // Send heartbeat every 5 seconds public: Game() { - InitWindow(1280, 720, "Multiplayer Terrain Game"); + InitWindow(1280, 720, "Game"); SetTargetFPS(60); // Initialize sky after window is created @@ -169,7 +169,7 @@ public: if (gameState == STATE_PLAYING && network.isConnected()) { network.sendLogout(); } - + UnloadTexture(terrainTexture); for (auto& [color, texture] : playerTextures) { UnloadTexture(texture); @@ -187,7 +187,7 @@ public: update(); render(); } - + // Clean logout when window is closing if (gameState == STATE_PLAYING && network.isConnected()) { network.sendLogout(); @@ -230,7 +230,7 @@ private: // Update sky sky->update(GetFrameTime()); - + // Time of day controls (for testing) if (IsKeyPressed(KEY_T)) { float currentTime = sky->getTimeOfDay(); @@ -284,7 +284,7 @@ private: network.sendMove(moveInput.x, 0, moveInput.z); lastHeartbeatTime = GetTime(); // Reset heartbeat timer when moving } - + // Send periodic heartbeats when not moving float currentTime = GetTime(); if (currentTime - lastHeartbeatTime >= HEARTBEAT_INTERVAL) { @@ -334,7 +334,7 @@ private: void render() { BeginDrawing(); - + // Clear with a default color first ClearBackground(SKYBLUE); @@ -400,7 +400,7 @@ private: void renderGame() { BeginMode3D(playerController.getCamera()); - + // Render skybox first (it will handle its own depth settings) if (sky) { sky->renderSkybox(playerController.getCamera()); @@ -433,13 +433,13 @@ private: std::string colorText = "Your color: " + network.getPlayerColor(); DrawText(colorText.c_str(), 10, 110, 20, WHITE); } - + // Show time of day float timeHour = sky->getTimeOfDay() * 24.0f; int hour = (int)timeHour; int minute = (int)((timeHour - hour) * 60); DrawText(TextFormat("Time: %02d:%02d", hour, minute), 10, 135, 20, WHITE); - + DrawFPS(10, 160); } }; diff --git a/client/shaders/sky.fs b/client/shaders/sky.fs new file mode 100644 index 0000000..2485a84 --- /dev/null +++ b/client/shaders/sky.fs @@ -0,0 +1,65 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragNormal; +in vec3 worldPos; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Custom uniforms for sky +uniform vec3 horizonColor; +uniform vec3 zenithColor; +uniform vec3 sunDirection; +uniform vec3 sunColor; +uniform float sunIntensity; + +// Output fragment color +out vec4 finalColor; + +void main() +{ + // Normalize the world position to get direction from center + vec3 direction = normalize(worldPos); + + // Calculate gradient based on Y component (height) + float gradientFactor = smoothstep(-0.5, 1.0, direction.y); + + // Interpolate between horizon and zenith colors + vec3 skyColor = mix(horizonColor, zenithColor, gradientFactor); + + // Calculate sun contribution + vec3 sunDir = normalize(sunDirection); + float sunDot = dot(direction, sunDir); + + // Sun rendering + vec3 color = skyColor; + + // Only render sun if it's above horizon and has intensity + if (sunDir.y > 0.0 && sunIntensity > 0.1) { + // Sun disc with soft edges + float sunSize = 0.99; // Smaller value = larger sun + float sunEdge = 0.98; + + if (sunDot > sunSize) { + // Bright sun core + color = mix(sunColor, vec3(1.0, 1.0, 0.95), 0.8); + } else if (sunDot > sunEdge) { + // Soft edge + float edge = smoothstep(sunEdge, sunSize, sunDot); + color = mix(skyColor, sunColor, edge); + } + + // Sun glow + if (sunDot > 0.85) { + float glow = pow(max(0.0, (sunDot - 0.85) / 0.15), 2.0) * sunIntensity; + color = mix(color, sunColor, glow * 0.5); + } + } + + finalColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/client/shaders/sky.vs b/client/shaders/sky.vs new file mode 100644 index 0000000..f410bbf --- /dev/null +++ b/client/shaders/sky.vs @@ -0,0 +1,34 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragNormal; +out vec3 worldPos; + +void main() +{ + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0))); + + // Store world position for sky calculations + worldPos = vertexPosition; + + // Calculate final vertex position + gl_Position = mvp * vec4(vertexPosition, 1.0); +} \ No newline at end of file diff --git a/client/sky/Sky.cpp b/client/sky/Sky.cpp index 90fffba..b6748e3 100644 --- a/client/sky/Sky.cpp +++ b/client/sky/Sky.cpp @@ -2,6 +2,7 @@ #include #include #include +#include Sky::Sky() : fogDensity(0.01f) @@ -14,8 +15,28 @@ Sky::Sky() createSkyDome(); updateColors(); - // Try to load a custom shader if available - // For now we'll use basic rendering + // 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"); + + // 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() { @@ -26,17 +47,11 @@ Sky::~Sky() { } 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); + // Create a large sphere for the sky dome + skyDome = GenMeshSphere(500.0f, 16, 16); - // 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); + // Don't invert normals - let's use backface culling instead + // Just upload the mesh as-is skyModel = LoadModelFromMesh(skyDome); } @@ -162,38 +177,58 @@ void Sky::renderSkybox(const Camera3D& camera) { // 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); - // 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 + if (shaderLoaded) { + // First make sure sun position is updated + sun.updatePosition(timeOfDay); - // Draw sun as a sphere - DrawSphere(sunPosOnSphere, 50.0f, sun.color); + // 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 + }; - // Draw sun glow layers - Color glowColor = sun.color; - glowColor.a = 80; - DrawSphere(sunPosOnSphere, 70.0f, glowColor); + // 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); + } - glowColor.a = 40; - DrawSphere(sunPosOnSphere, 90.0f, glowColor); + + // 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); + + // 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(); } diff --git a/client/sky/Sky.hpp b/client/sky/Sky.hpp index 6406fb6..a5c450b 100644 --- a/client/sky/Sky.hpp +++ b/client/sky/Sky.hpp @@ -2,7 +2,6 @@ #include #include -#include class Sky { private: @@ -12,7 +11,7 @@ private: Color color; float intensity; float angle; // Sun angle for day/night cycle - + void updatePosition(float timeOfDay); Vector3 getLightDirection() const { return direction; } }; @@ -23,12 +22,19 @@ private: Color fogColor; float fogDensity; float timeOfDay; // 0.0 to 1.0 (0 = midnight, 0.5 = noon) - + Shader skyShader; Mesh skyDome; Model skyModel; bool shaderLoaded; + // Shader locations + int horizonColorLoc; + int zenithColorLoc; + int sunDirectionLoc; + int sunColorLoc; + int sunIntensityLoc; + public: Sky(); ~Sky(); @@ -36,23 +42,23 @@ public: 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 +};