This commit is contained in:
Masashi 2025-12-18 21:16:16 +05:30
commit 0151eb65ef
3 changed files with 149 additions and 7 deletions

Binary file not shown.

View file

@ -1,4 +1,4 @@
// game/client.c - SMOOTH TERRAIN VERSION
// game/client.c - SMOOTH TERRAIN VERSION WITH DIRECTIONAL LIGHT & SHADOWS
#include "raylib.h"
#include <math.h>
#include <stdint.h>
@ -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();

Binary file not shown.