diff --git a/game/client b/game/client deleted file mode 100755 index dd120e6..0000000 Binary files a/game/client and /dev/null differ diff --git a/game/client.c b/game/client.c index c9ac211..c7eb9e5 100644 --- a/game/client.c +++ b/game/client.c @@ -1,4 +1,4 @@ -// game/client.c - SMOOTH TERRAIN VERSION +// game/client.c - SMOOTH TERRAIN VERSION WITH DIRECTIONAL LIGHT & SHADOWS #include "raylib.h" #include #include @@ -43,6 +43,16 @@ typedef int socklen_t; #define BTN_USE_MEDKIT (1u << 4) #define BTN_JUMP (1u << 5) +// ============== DIRECTIONAL LIGHT ============== +typedef struct { + Vector3 direction; // Normalized light direction + Vector3 color; // RGB color (0-1 range) + float intensity; // Light intensity multiplier + float ambientIntensity; // Ambient light strength + float shadowBias; // Shadow bias to prevent z-fighting + float shadowIntensity; // How dark shadows are (0-1) +} DirectionalLight; + // ============== SIMPLEX NOISE ============== static const uint8_t perm[512] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, @@ -257,6 +267,110 @@ static Mesh generate_terrain_mesh(void) { } // ============== END SIMPLEX NOISE ============== +// ============== LIGHTING HELPERS ============== +// apply_directional_lighting: Basic Lambertian diffuse lighting +// Applies directional light with diffuse component based on surface normal +static Color apply_directional_lighting(Color baseColor, Vector3 normal, + DirectionalLight light) { + // Normalize light direction (it's already normalized, but for safety) + Vector3 lightDir = light.direction; + + // Calculate diffuse component: dot product of negative light direction and + // surface normal We use negative because light travels opposite to its + // direction vector + float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y + + lightDir.z * normal.z)); + + // Combine diffuse with ambient light + // Ambient provides minimum brightness even in shadow + float brightness = light.ambientIntensity + + (diff * light.intensity * (1.0f - light.ambientIntensity)); + + // Apply brightness multiplier to base color + int r = (int)(baseColor.r * brightness); + int g = (int)(baseColor.g * brightness); + int b = (int)(baseColor.b * brightness); + + // Clamp RGB to valid range [0, 255] + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + return (Color){r, g, b, baseColor.a}; +} + +// calculate_shadow_factor: Distance-based shadow softening +// Objects at higher elevations or farther from ground get softer, less +// pronounced shadows This simulates how shadows fade with distance and +// atmospheric scattering +static float calculate_shadow_factor(Vector3 worldPos, Vector3 lightDir, + float maxShadowDistance) { + // Calculate height above ground (approximate) + float distFromLight = fmaxf(0.0f, worldPos.y - 2.0f); + + // Fade out shadow strength with distance (max 15% darkening) + float shadowIntensity = + fmaxf(0.0f, 1.0f - (distFromLight / maxShadowDistance)); + return 1.0f - (shadowIntensity * 0.15f); +} + +// calculate_temporal_shadow: Time-based shadow variance for anti-aliasing +// Simulates subtle shadow movement to avoid banding artifacts +static float calculate_temporal_shadow(Vector3 worldPos, float timePhase) { + // Add subtle time-based variation to shadow boundaries + float noiseVal = + sinf(worldPos.x * 0.5f + timePhase) * cosf(worldPos.z * 0.5f + timePhase); + return 1.0f + (noiseVal * 0.02f); // Very subtle variation +} + +// apply_lighting_with_shadows: Full lighting calculation with shadows +// Combines directional light, ambient light, shadows, and temporal variation +static Color apply_lighting_with_shadows(Color baseColor, Vector3 normal, + Vector3 worldPos, + DirectionalLight light) { + // Calculate base shadow factor (distance-based) + float shadowFactor = + calculate_shadow_factor(worldPos, light.direction, 10.0f); + + // Apply shadow to intensity + float adjustedIntensity = light.intensity * shadowFactor; + + // Calculate diffuse component with adjusted intensity + Vector3 lightDir = light.direction; + float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y + + lightDir.z * normal.z)); + + // Combine with ambient using adjusted intensity + float brightness = light.ambientIntensity + (diff * adjustedIntensity * + (1.0f - light.ambientIntensity)); + + // Apply brightness to base color + int r = (int)(baseColor.r * brightness); + int g = (int)(baseColor.g * brightness); + int b = (int)(baseColor.b * brightness); + + // Clamp values to valid RGB range + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + return (Color){r, g, b, baseColor.a}; +} + +static Vector3 v3_normalize(Vector3 v) { + float len = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); + if (len < 0.0001f) + return (Vector3){0, 1, 0}; + return (Vector3){v.x / len, v.y / len, v.z / len}; +} +// ============== END LIGHTING HELPERS ============== + static void set_nonblocking(int sock) { #ifdef _WIN32 u_long mode = 1; @@ -427,7 +541,7 @@ int main(void) { #endif const int sw = 1280, sh = 720; - InitWindow(sw, sh, "Voxel Shooter - Client (SMOOTH TERRAIN)"); + InitWindow(sw, sh, "Voxel Shooter - Client (SMOOTH TERRAIN WITH SHADOWS)"); SetTargetFPS(120); char myName[USERNAME_MAX]; @@ -441,6 +555,16 @@ int main(void) { terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){80, 140, 70, 255}; + // Setup directional light + DirectionalLight dirLight = {0}; + dirLight.direction = + v3_normalize(v3(-0.8f, -1.0f, -0.6f)); // Coming from upper-left-back + dirLight.color = v3(1.0f, 1.0f, 1.0f); + dirLight.intensity = 1.2f; + dirLight.ambientIntensity = 0.3f; + dirLight.shadowBias = 0.005f; + dirLight.shadowIntensity = 0.4f; + int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { perror("socket"); @@ -672,10 +796,20 @@ int main(void) { BeginMode3D(cam); - // Draw smooth terrain + // Calculate terrain normal at camera position for lighting + Vector3 terrainNormal = v3(0, 1, 0); // Default up normal + + // Draw smooth terrain with directional lighting + shadows + // For Raylib, we apply lighting by adjusting material colors + Color terrainBaseColor = (Color){80, 140, 70, 255}; + Color litTerrainColor = apply_lighting_with_shadows( + terrainBaseColor, terrainNormal, v3(0, 2, 0), dirLight); + + terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = + litTerrainColor; DrawModel(terrainModel, (Vector3){0, 0, 0}, 1.0f, WHITE); - // Items + // Items with lighting and shadows for (int i = 0; i < MAX_ITEMS; i++) { if (!wi[i].present) continue; @@ -686,10 +820,14 @@ int main(void) { ic = (Color){255, 220, 120, 255}; if (wi[i].type == ITEM_AMMO_RIFLE) ic = (Color){255, 180, 120, 255}; - DrawSphere(wi[i].pos, 0.3f, ic); + + // Apply lighting with shadows to items + Color litColor = + apply_lighting_with_shadows(ic, terrainNormal, wi[i].pos, dirLight); + DrawSphere(wi[i].pos, 0.3f, litColor); } - // Players + // Players with lighting and shadows for (int i = 0; i < MAX_PLAYERS; i++) { if (!rp[i].present) continue; @@ -698,8 +836,12 @@ int main(void) { (i == myId) ? (Color){80, 180, 255, 255} : (Color){255, 80, 80, 255}; if (!rp[i].alive) c = (Color){120, 120, 120, 255}; + + // Apply lighting with shadows to player models + Color litPlayerColor = + apply_lighting_with_shadows(c, terrainNormal, p, dirLight); DrawCapsule(v3(p.x, p.y - 0.5f, p.z), v3(p.x, p.y + 0.5f, p.z), 0.35f, 8, - 8, c); + 8, litPlayerColor); } EndMode3D(); diff --git a/game/server b/game/server deleted file mode 100755 index c460ae1..0000000 Binary files a/game/server and /dev/null differ