diff --git a/game/client b/game/client index 09033d3..b57a50b 100755 Binary files a/game/client and b/game/client differ diff --git a/game/client.c b/game/client.c index 56670b4..671b11e 100644 --- a/game/client.c +++ b/game/client.c @@ -181,8 +181,8 @@ float get_terrain_height(float x, float z) { return height * 6.0f + 2.0f; } -#define TERRAIN_SIZE 64 -#define TERRAIN_SCALE 2.0f +#define TERRAIN_SIZE 128 +#define TERRAIN_SCALE 1.0f static Mesh generate_terrain_mesh(void) { int size = TERRAIN_SIZE; @@ -245,23 +245,29 @@ static Mesh generate_terrain_mesh(void) { int idx = z * size + x; float wx = ((float)x - size / 2.0f) * TERRAIN_SCALE; float wz = ((float)z - size / 2.0f) * TERRAIN_SCALE; - + // Sample height gradients to compute terrain normal // Use neighboring vertices for finite difference approximation - float h_right = (x + 1 < size) ? mesh.vertices[(z * size + (x + 1)) * 3 + 1] : mesh.vertices[idx * 3 + 1]; - float h_left = (x - 1 >= 0) ? mesh.vertices[(z * size + (x - 1)) * 3 + 1] : mesh.vertices[idx * 3 + 1]; - float h_down = (z + 1 < size) ? mesh.vertices[((z + 1) * size + x) * 3 + 1] : mesh.vertices[idx * 3 + 1]; - float h_up = (z - 1 >= 0) ? mesh.vertices[((z - 1) * size + x) * 3 + 1] : mesh.vertices[idx * 3 + 1]; - + float h_right = (x + 1 < size) + ? mesh.vertices[(z * size + (x + 1)) * 3 + 1] + : mesh.vertices[idx * 3 + 1]; + float h_left = (x - 1 >= 0) ? mesh.vertices[(z * size + (x - 1)) * 3 + 1] + : mesh.vertices[idx * 3 + 1]; + float h_down = (z + 1 < size) + ? mesh.vertices[((z + 1) * size + x) * 3 + 1] + : mesh.vertices[idx * 3 + 1]; + float h_up = (z - 1 >= 0) ? mesh.vertices[((z - 1) * size + x) * 3 + 1] + : mesh.vertices[idx * 3 + 1]; + // Compute finite differences float dh_dx = (h_right - h_left) / (2.0f * TERRAIN_SCALE); float dh_dz = (h_down - h_up) / (2.0f * TERRAIN_SCALE); - + // Normal from height field: (-dh/dx, 1, -dh/dz) then normalized float nx = -dh_dx; float ny = 1.0f; float nz = -dh_dz; - + float len = sqrtf(nx * nx + ny * ny + nz * nz); if (len > 0.0001f) { mesh.normals[idx * 3 + 0] = nx / len; @@ -377,11 +383,11 @@ static Color apply_lighting_with_shadows(Color baseColor, Vector3 normal, } 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}; - } + 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}; +} // ============== VECTOR3 HELPERS ============== static Vector3 v3(float x, float y, float z) { @@ -410,24 +416,25 @@ static Vector3 v3_norm(Vector3 a) { // ============== ENHANCED LIGHTING & AMBIENT OCCLUSION ============== -// calculate_ambient_occlusion: Estimates how occluded a point is based on terrain curvature -// Samples heights in cardinal directions and computes horizon angle to terrain -// Returns occlusion factor from 0 (fully occluded) to 1 (fully lit) +// calculate_ambient_occlusion: Estimates how occluded a point is based on +// terrain curvature Samples heights in cardinal directions and computes horizon +// angle to terrain Returns occlusion factor from 0 (fully occluded) to 1 (fully +// lit) static float calculate_ambient_occlusion(Vector3 worldPos, Vector3 normal) { // AO based on terrain curvature: check if terrain rises around the point float sampleRadius = 3.0f; float aoAccum = 0.0f; int numSamples = 8; - + float centerHeight = worldPos.y; - + // Sample 8 directions around the point for (int i = 0; i < numSamples; i++) { float angle = (2.0f * 3.14159265f * (float)i) / (float)numSamples; float sx = worldPos.x + cosf(angle) * sampleRadius; float sz = worldPos.z + sinf(angle) * sampleRadius; float sh = get_terrain_height(sx, sz); - + // Check if terrain is higher relative to surface normal // Higher terrain in shadow-casting areas reduces occlusion float heightDiff = sh - centerHeight; @@ -437,53 +444,63 @@ static float calculate_ambient_occlusion(Vector3 worldPos, Vector3 normal) { aoAccum += aoAmount; } } - - float aoFactor = 1.0f - (aoAccum / (float)numSamples) * 0.6f; // 60% max occlusion - return fmaxf(0.2f, aoFactor); // Min 20% brightness + + float aoFactor = + 1.0f - (aoAccum / (float)numSamples) * 0.6f; // 60% max occlusion + return fmaxf(0.2f, aoFactor); // Min 20% brightness } -// apply_phong_lighting_per_pixel: Advanced Phong lighting with per-pixel normals -// Includes diffuse, specular highlight, and ambient occlusion for geometry detail +// apply_phong_lighting_per_pixel: Advanced Phong lighting with per-pixel +// normals Includes diffuse, specular highlight, and ambient occlusion for +// geometry detail static Color apply_phong_lighting_per_pixel(Color baseColor, Vector3 normal, Vector3 worldPos, Vector3 camPos, DirectionalLight light) { // Normalize inputs Vector3 lightDir = v3_normalize(light.direction); Vector3 normal_norm = v3_normalize(normal); - + // Compute view direction (from surface to camera) Vector3 viewDir = v3_normalize(v3_sub(camPos, worldPos)); - + // Diffuse component: Lambertian shading float diffuse = fmaxf(0.0f, -v3_dot(lightDir, normal_norm)); - + // Specular component: Blinn-Phong specular highlight - Vector3 halfVec = v3_normalize(v3_add(v3_norm((Vector3){-lightDir.x, -lightDir.y, -lightDir.z}), viewDir)); - float specular = powf(fmaxf(0.0f, v3_dot(halfVec, normal_norm)), 32.0f) * 0.5f; - + Vector3 halfVec = v3_normalize(v3_add( + v3_norm((Vector3){-lightDir.x, -lightDir.y, -lightDir.z}), viewDir)); + float specular = + powf(fmaxf(0.0f, v3_dot(halfVec, normal_norm)), 32.0f) * 0.5f; + // Ambient occlusion from terrain geometry float ao = calculate_ambient_occlusion(worldPos, normal_norm); - + // Shadow based on height (distant higher terrain casts softer shadows) - float shadowFactor = calculate_shadow_factor(worldPos, light.direction, 10.0f); - + float shadowFactor = + calculate_shadow_factor(worldPos, light.direction, 10.0f); + // Combine lighting components float brightness = light.ambientIntensity * ao; - brightness += diffuse * light.intensity * shadowFactor * (1.0f - light.ambientIntensity) * ao; - brightness += specular * light.intensity * shadowFactor * 0.6f; // Specular less affected by AO - + brightness += diffuse * light.intensity * shadowFactor * + (1.0f - light.ambientIntensity) * ao; + brightness += specular * light.intensity * shadowFactor * + 0.6f; // Specular less affected by AO + brightness = fminf(1.0f, brightness); - + // Apply brightness to base color int r = (int)(baseColor.r * brightness); int g = (int)(baseColor.g * brightness); int b = (int)(baseColor.b * brightness); - + // Clamp to valid RGB range - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + return (Color){r, g, b, baseColor.a}; } @@ -663,9 +680,12 @@ int main(void) { // Load terrain shader TerrainShader terrainShader = load_terrain_shader(); if (terrainShader.shader.id == 0) { - fprintf(stderr, "Warning: Failed to load terrain shader, using default rendering\n"); + fprintf( + stderr, + "Warning: Failed to load terrain shader, using default rendering\n"); // Fallback: Make terrain very bright red to show shader failed - terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){255, 100, 100, 255}; + terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = + (Color){255, 100, 100, 255}; } else { terrainModel.materials[0].shader = terrainShader.shader; fprintf(stderr, "Shader loaded successfully!\n"); @@ -692,12 +712,10 @@ int main(void) { Vector3 camPos = v3(0, 5.0f, 6); float yaw = 0.0f, pitch = 0.0f; - float recoilYaw = 0.0f, recoilPitch = 0.0f, crossSpread = 0.0f; - float fireCooldown = 0.0f; - int shotsInBurst = 0; - float burstResetTimer = 0.0f; - - + float recoilYaw = 0.0f, recoilPitch = 0.0f, crossSpread = 0.0f; + float fireCooldown = 0.0f; + int shotsInBurst = 0; + float burstResetTimer = 0.0f; const float pistolFireRate = 4.0f, rifleFireRate = 12.0f; const float recoilReturn = 18.0f, crossReturn = 14.0f; @@ -790,6 +808,10 @@ int main(void) { camPos.x = body.x; camPos.y = body.y + 1.0f; camPos.z = body.z + 0.0001f; + + // Prevent camera from clipping into terrain + float terrain_h = get_terrain_height(camPos.x, camPos.z); + camPos.y = fmaxf(camPos.y, terrain_h + 1.5f); } } else if (type == MSG_ITEMS && n >= (int)sizeof(MsgItems)) { MsgItems *m = (MsgItems *)buf; @@ -846,8 +868,6 @@ int main(void) { if (IsKeyPressed(KEY_SPACE)) buttons |= BTN_JUMP; - - if (myId != 255) { MsgInput in = {0}; in.type = MSG_INPUT; @@ -908,25 +928,36 @@ int main(void) { // Set up shader uniforms for terrain rendering if (terrainShader.shader.id != 0) { - // Convert light direction to shader format (should be pointing TO the light) - float lightDirArray[3] = {-dirLight.direction.x, -dirLight.direction.y, -dirLight.direction.z}; - float lightColorArray[3] = {dirLight.color.x, dirLight.color.y, dirLight.color.z}; + // Convert light direction to shader format (should be pointing TO the + // light) + float lightDirArray[3] = {-dirLight.direction.x, -dirLight.direction.y, + -dirLight.direction.z}; + float lightColorArray[3] = {dirLight.color.x, dirLight.color.y, + dirLight.color.z}; float viewPosArray[3] = {camPos.x, camPos.y, camPos.z}; // Terrain color in 0-1 range (80, 140, 70) / 255 - float terrainColorArray[3] = {80.0f/255.0f, 140.0f/255.0f, 70.0f/255.0f}; + float terrainColorArray[3] = {80.0f / 255.0f, 140.0f / 255.0f, + 70.0f / 255.0f}; // Debug mode: 0=normal lighting, 1=show normals as colors, 2=show AO only - SetShaderValue(terrainShader.shader, terrainShader.locViewPos, viewPosArray, SHADER_UNIFORM_VEC3); - SetShaderValue(terrainShader.shader, terrainShader.locLightDir, lightDirArray, SHADER_UNIFORM_VEC3); - SetShaderValue(terrainShader.shader, terrainShader.locLightColor, lightColorArray, SHADER_UNIFORM_VEC3); - SetShaderValue(terrainShader.shader, terrainShader.locLightIntensity, &dirLight.intensity, SHADER_UNIFORM_FLOAT); - SetShaderValue(terrainShader.shader, terrainShader.locAmbientIntensity, &dirLight.ambientIntensity, SHADER_UNIFORM_FLOAT); - SetShaderValue(terrainShader.shader, terrainShader.locTerrainColor, terrainColorArray, SHADER_UNIFORM_VEC3); + SetShaderValue(terrainShader.shader, terrainShader.locViewPos, + viewPosArray, SHADER_UNIFORM_VEC3); + SetShaderValue(terrainShader.shader, terrainShader.locLightDir, + lightDirArray, SHADER_UNIFORM_VEC3); + SetShaderValue(terrainShader.shader, terrainShader.locLightColor, + lightColorArray, SHADER_UNIFORM_VEC3); + SetShaderValue(terrainShader.shader, terrainShader.locLightIntensity, + &dirLight.intensity, SHADER_UNIFORM_FLOAT); + SetShaderValue(terrainShader.shader, terrainShader.locAmbientIntensity, + &dirLight.ambientIntensity, SHADER_UNIFORM_FLOAT); + SetShaderValue(terrainShader.shader, terrainShader.locTerrainColor, + terrainColorArray, SHADER_UNIFORM_VEC3); } - + // Draw terrain with shader - terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){80, 140, 70, 255}; + terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = + (Color){80, 140, 70, 255}; DrawModel(terrainModel, (Vector3){0, 0, 0}, 1.0f, WHITE); // Items with basic lighting (no shader for now to keep it simple) @@ -943,7 +974,8 @@ int main(void) { // Apply basic lighting to items Vector3 itemNormal = v3(0, 1, 0); - Color litColor = apply_lighting_with_shadows(ic, itemNormal, wi[i].pos, dirLight); + Color litColor = + apply_lighting_with_shadows(ic, itemNormal, wi[i].pos, dirLight); DrawSphere(wi[i].pos, 0.3f, litColor); } @@ -959,7 +991,8 @@ int main(void) { // Apply basic lighting to players Vector3 playerNormal = v3(0, 1, 0); - Color litPlayerColor = apply_lighting_with_shadows(c, playerNormal, p, dirLight); + Color litPlayerColor = + apply_lighting_with_shadows(c, playerNormal, 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, litPlayerColor); } diff --git a/game/server b/game/server index 1b9cead..f9cddc2 100755 Binary files a/game/server and b/game/server differ diff --git a/game/server.c b/game/server.c index c497376..f042c43 100644 --- a/game/server.c +++ b/game/server.c @@ -144,11 +144,11 @@ static float fbm_noise(float x, float y, int octaves) { float get_terrain_height(float x, float z) { float height = fbm_noise(x * 0.03f, z * 0.03f, 4); height += fbm_noise(x * 0.01f, z * 0.01f, 2) * 1.5f; - return height * 6.0f + 2.0f; + return height * 5.0f + 2.0f; } -#define TERRAIN_SIZE 64 -#define TERRAIN_SCALE 2.0f +#define TERRAIN_SIZE 128 +#define TERRAIN_SCALE 1.0f static dReal *gHeightData = NULL; static dGeomID gTerrainGeom = NULL; @@ -712,7 +712,8 @@ int main(void) { if (m->buttons & BTN_JUMP) { const dReal *lvel = dBodyGetLinearVel(p->body); - dBodySetLinearVel(p->body, lvel[0], 12.0f, lvel[2]); // Set upward velocity + dBodySetLinearVel(p->body, lvel[0], 12.0f, + lvel[2]); // Set upward velocity printf("Player %d jumped\n", pid); } }