BETTER TERRAIN
This commit is contained in:
parent
75ee5a6da8
commit
a092c10017
4 changed files with 106 additions and 72 deletions
BIN
game/client
BIN
game/client
Binary file not shown.
169
game/client.c
169
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);
|
||||
}
|
||||
|
|
|
|||
BIN
game/server
BIN
game/server
Binary file not shown.
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue