This commit is contained in:
Masashi 2025-12-20 15:09:29 +05:30
commit 0c0cfef186
28 changed files with 220 additions and 331 deletions

View file

@ -1,24 +1,22 @@
#!/bin/bash
# Build the game client using the split modules
set -e
cd "$(dirname "$0")"
# Get raylib compile and link flags
RAYLIB_CFLAGS=$(pkg-config --cflags raylib 2>/dev/null || echo "-I/usr/include")
RAYLIB_LIBS=$(pkg-config --libs raylib 2>/dev/null || echo "-lraylib -lm")
echo "Compiling game modules..."
gcc -c suic_math.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_math.o
gcc -c suic_terrain.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_terrain.o
gcc -c suic_lighting.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_lighting.o
gcc -c suic_net.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_net.o
gcc -c suic_ui.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_ui.o
gcc -c suic_game_client.c -I../libsuicmez $RAYLIB_CFLAGS -o suic_game_client.o
gcc -c inc/suic_math.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_math.o
gcc -c inc/suic_terrain.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_terrain.o
gcc -c inc/suic_lighting.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_lighting.o
gcc -c inc/suic_net.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_net.o
gcc -c inc/suic_ui.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_ui.o
gcc -c inc/suic_game_client.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o suic_game_client.o
echo "Compiling main entry point..."
gcc -c main.c -I../libsuicmez $RAYLIB_CFLAGS -o main.o
gcc -c main.c -Iinc -I../libsuicmez $RAYLIB_CFLAGS -o main.o
echo "Linking..."
gcc main.o suic_math.o suic_terrain.o suic_lighting.o suic_net.o suic_ui.o suic_game_client.o \

1
game/clean.sh Executable file
View file

@ -0,0 +1 @@
rm *.o

View file

@ -1,60 +0,0 @@
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void* gc_alloc(const TypeInfo* type, size_t size);
void gc_init(void);
void gc_shutdown(void);
// Helper for allocating arrays
static void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) {
void* ptr = gc_alloc(type, elem_size * len);
if (init_data) memcpy(ptr, init_data, elem_size * len);
return ptr;
}
// Helper for allocating structs
static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) {
void* ptr = gc_alloc(type, size);
if (init_data) memcpy(ptr, init_data, size);
return ptr;
}
;
;
int suic_main(void);
int suic_main(void) {
suic_init_window(1280, 720, suic_alloc_array(NULL, sizeof(char), 21, "Voxel Shooter Client"));
suic_set_target_fps(120);
suic_disable_cursor();
while ((suic_window_should_close() == false)) {
float dt = suic_get_frame_time();
suic_begin_drawing();
suic_clear_background(135, 206, 235, 255);
DrawFPS(10, 10);
suic_end_drawing();
}
suic_close_window();
return 0;
}
int main(int argc, char* argv[]) {
// Initialize GC
gc_init();
// init globals
// init event loop
int result = suic_main();
// Shutdown GC
gc_shutdown();
return result;
}

Binary file not shown.

19
game/inc/suic_app.h Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
void suic_runtime_init(void);
void suic_runtime_shutdown(void);
void suic_app_init(int width, int height, const char *title);
bool suic_app_should_close(void);
void suic_app_frame_begin(void);
void suic_app_frame_end(void);
void suic_app_shutdown(void);
void suic_draw_hello(void);
const char *suic_item_name(uint8_t t);
struct Color *suic_apply_directional_lighting(struct Color *baseColor,
struct Vector3 *normal,
struct DirectionalLight *light);

View file

@ -3,8 +3,6 @@
#include <math.h>
#include <string.h>
/* ===== GAME STATE ===== */
static SuicVec3 g_camPos = {0, 5.0f, 6};
static float g_yaw = 0.0f, g_pitch = 0.0f;
static SuicVec3 g_prevBodyPos = {0, 5.0f, 6};
@ -29,8 +27,6 @@ static const float PISTOL_CROSS_KICK = 2.0f, RIFLE_CROSS_KICK = 4.0f;
static const float PISTOL_SPRAY_GROW = 0.4f, RIFLE_SPRAY_GROW = 1.2f;
static const float BURST_RESET_TIME = 0.18f;
/* ===== LIFECYCLE ===== */
void suic_game_init(const char* username, const char* serverIp, int port) {
suic_terrain_init();
suic_net_init(serverIp, port);
@ -47,8 +43,6 @@ void suic_game_shutdown(void) {
suic_net_shutdown();
}
/* ===== INPUT HANDLING ===== */
void suic_game_handle_input(float dt) {
g_clientTick++;
@ -126,8 +120,6 @@ void suic_game_handle_input(float dt) {
}
}
/* ===== UPDATE ===== */
void suic_game_update(float dt) {
suic_net_poll();
@ -156,19 +148,19 @@ void suic_game_update(float dt) {
g_interpTimer += dt;
/* Forward offset */
float viewYaw = g_yaw + g_recoilYaw;
float viewPitch = g_pitch + g_recoilPitch;
SuicVec3 forward = suic_v3(sinf(viewYaw) * cosf(viewPitch), sinf(viewPitch),
cosf(viewYaw) * cosf(viewPitch));
g_camPos = suic_v3_add(g_camPos, suic_v3_mul(forward, 0.3f));
g_camPos.y -= 0.2f;
if (myId != 255) {
float viewYaw = g_yaw + g_recoilYaw;
float viewPitch = g_pitch + g_recoilPitch;
SuicVec3 forward = suic_v3(sinf(viewYaw) * cosf(viewPitch), sinf(viewPitch),
cosf(viewYaw) * cosf(viewPitch));
g_camPos = suic_v3_add(g_camPos, suic_v3_mul(forward, 0.3f));
g_camPos.y -= 0.2f;
}
float terrain_h = suic_terrain_height(g_camPos.x, g_camPos.z);
g_camPos.y = fmaxf(g_camPos.y, terrain_h + 1.5f);
}
/* ===== RENDERING ===== */
void suic_game_render(void) {
int sw = GetScreenWidth();
int sh = GetScreenHeight();
@ -252,7 +244,7 @@ void suic_game_render(void) {
/* HUD */
if (myId == 255 || !suic_net_player_present(myId)) {
DrawText("Connecting...", 10, 10, 20, RAYWHITE);
DrawText("Offline", 10, 10, 20, RAYWHITE);
} else {
int weapon = suic_net_player_weapon(myId);
int mag = (weapon == SUIC_WEAPON_PISTOL) ? suic_net_player_pistol_mag(myId)
@ -272,8 +264,6 @@ void suic_game_render(void) {
suic_ui_draw_crosshair(sw, sh, g_crossSpread, g_scoped);
}
/* ===== ACCESSORS ===== */
SuicVec3 suic_game_get_camera_pos(void) { return g_camPos; }
float suic_game_get_yaw(void) { return g_yaw; }
float suic_game_get_pitch(void) { return g_pitch; }

153
game/inc/suic_lighting.c Normal file
View file

@ -0,0 +1,153 @@
#include "suic_lighting.h"
#include "suic_terrain.h"
#include <math.h>
SuicDirectionalLight suic_light_create(float dir_x, float dir_y, float dir_z,
float intensity, float ambient) {
SuicDirectionalLight light = {0};
light.direction = suic_v3_norm(suic_v3(dir_x, dir_y, dir_z));
light.color = suic_v3(1.0f, 1.0f, 1.0f);
light.intensity = intensity;
light.ambientIntensity = ambient;
light.shadowBias = 0.005f;
light.shadowIntensity = 0.4f;
return light;
}
SuicDirectionalLight suic_light_create_default(void) {
return suic_light_create(-0.8f, -1.0f, -0.6f, 1.2f, 0.3f);
}
SuicColor suic_light_apply(SuicColor base, SuicVec3 normal,
SuicDirectionalLight *light) {
SuicVec3 lightDir = light->direction;
/* Diffuse: Lambertian */
float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y +
lightDir.z * normal.z));
float brightness =
light->ambientIntensity +
(diff * light->intensity * (1.0f - light->ambientIntensity));
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
return (SuicColor){r, g, b, base.a};
}
float suic_shadow_factor(SuicVec3 worldPos, SuicVec3 lightDir, float maxDist) {
float distFromLight = fmaxf(0.0f, worldPos.y - 2.0f);
float shadowIntensity = fmaxf(0.0f, 1.0f - (distFromLight / maxDist));
return 1.0f - (shadowIntensity * 0.15f);
}
float suic_shadow_temporal(SuicVec3 worldPos, float timePhase) {
float noiseVal =
sinf(worldPos.x * 0.5f + timePhase) * cosf(worldPos.z * 0.5f + timePhase);
return 1.0f + (noiseVal * 0.02f);
}
SuicColor suic_light_apply_shadows(SuicColor base, SuicVec3 normal,
SuicVec3 worldPos,
SuicDirectionalLight *light) {
float shadowFactor = suic_shadow_factor(worldPos, light->direction, 10.0f);
float adjustedIntensity = light->intensity * shadowFactor;
SuicVec3 lightDir = light->direction;
float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y +
lightDir.z * normal.z));
float brightness =
light->ambientIntensity +
(diff * adjustedIntensity * (1.0f - light->ambientIntensity));
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
return (SuicColor){r, g, b, base.a};
}
float suic_ao_calculate(SuicVec3 worldPos, SuicVec3 normal) {
float sampleRadius = 3.0f;
float aoAccum = 0.0f;
int numSamples = 8;
float centerHeight = worldPos.y;
float pi = 3.14159265f;
for (int i = 0; i < numSamples; i++) {
float angle = (2.0f * pi * (float)i) / (float)numSamples;
float sx = worldPos.x + cosf(angle) * sampleRadius;
float sz = worldPos.z + sinf(angle) * sampleRadius;
float sh = suic_terrain_height(sx, sz);
float heightDiff = sh - centerHeight;
if (heightDiff > 0.1f) {
float aoAmount = fminf(1.0f, heightDiff / 2.0f);
aoAccum += aoAmount;
}
}
float aoFactor = 1.0f - (aoAccum / (float)numSamples) * 0.6f;
return fmaxf(0.2f, aoFactor);
}
SuicColor suic_light_apply_phong(SuicColor base, SuicVec3 normal,
SuicVec3 worldPos, SuicVec3 camPos,
SuicDirectionalLight *light) {
SuicVec3 lightDir = suic_v3_norm(light->direction);
SuicVec3 normal_n = suic_v3_norm(normal);
SuicVec3 viewDir = suic_v3_norm(suic_v3_sub(camPos, worldPos));
/* Diffuse */
float diffuse = fmaxf(0.0f, -suic_v3_dot(lightDir, normal_n));
/* Specular: Blinn-Phong */
SuicVec3 negLight = suic_v3(-lightDir.x, -lightDir.y, -lightDir.z);
SuicVec3 halfVec = suic_v3_norm(suic_v3_add(suic_v3_norm(negLight), viewDir));
float specular =
powf(fmaxf(0.0f, suic_v3_dot(halfVec, normal_n)), 32.0f) * 0.5f;
/* Ambient occlusion */
float ao = suic_ao_calculate(worldPos, normal_n);
/* Shadow */
float shadowFactor = suic_shadow_factor(worldPos, light->direction, 10.0f);
/* Combine */
float brightness = light->ambientIntensity * ao;
brightness += diffuse * light->intensity * shadowFactor *
(1.0f - light->ambientIntensity) * ao;
brightness += specular * light->intensity * shadowFactor * 0.6f;
brightness = fminf(1.0f, brightness);
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
return (SuicColor){r, g, b, base.a};
}

View file

@ -3,6 +3,7 @@
#include <math.h>
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
/* ===== SIMPLEX NOISE IMPLEMENTATION ===== */
@ -208,8 +209,12 @@ void suic_terrain_init(void) {
g_terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){80, 140, 70, 255};
g_terrainShader = suic_terrain_shader_load();
if (g_terrainShader.shaderId != 0) {
g_terrainModel.materials[0].shader.id = g_terrainShader.shaderId;
if (g_terrainShader.shader.id != 0) {
g_terrainModel.materials[0].shader = g_terrainShader.shader;
fprintf(stderr, "Terrain shader loaded successfully!\n");
} else {
fprintf(stderr, "Warning: Failed to load terrain shader, using default rendering\n");
g_terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){255, 100, 100, 255};
}
g_terrainInitialized = true;
@ -219,7 +224,7 @@ void suic_terrain_draw(SuicVec3 cameraPos) {
if (!g_terrainInitialized) return;
/* Set shader uniforms if shader is loaded */
if (g_terrainShader.shaderId != 0) {
if (g_terrainShader.shader.id != 0) {
SuicVec3 lightDir = suic_v3_norm(suic_v3(-0.8f, -1.0f, -0.6f));
SuicVec3 lightColor = suic_v3(1.0f, 1.0f, 1.0f);
suic_terrain_shader_set_uniforms(&g_terrainShader, cameraPos, lightDir, lightColor, 1.2f, 0.3f);
@ -240,44 +245,41 @@ void suic_terrain_cleanup(void) {
SuicTerrainShader suic_terrain_shader_load(void) {
SuicTerrainShader ts = {0};
Shader shader = LoadShader("terrain.vs", "terrain.fs");
ts.shaderId = shader.id;
ts.shader = LoadShader("terrain.vs", "terrain.fs");
if (shader.id != 0) {
ts.locViewPos = GetShaderLocation(shader, "viewPos");
ts.locLightDir = GetShaderLocation(shader, "lightDir");
ts.locLightColor = GetShaderLocation(shader, "lightColor");
ts.locLightIntensity = GetShaderLocation(shader, "lightIntensity");
ts.locAmbientIntensity = GetShaderLocation(shader, "ambientIntensity");
ts.locTerrainColor = GetShaderLocation(shader, "terrainColor");
if (ts.shader.id != 0) {
ts.locViewPos = GetShaderLocation(ts.shader, "viewPos");
ts.locLightDir = GetShaderLocation(ts.shader, "lightDir");
ts.locLightColor = GetShaderLocation(ts.shader, "lightColor");
ts.locLightIntensity = GetShaderLocation(ts.shader, "lightIntensity");
ts.locAmbientIntensity = GetShaderLocation(ts.shader, "ambientIntensity");
ts.locTerrainColor = GetShaderLocation(ts.shader, "terrainColor");
}
return ts;
}
void suic_terrain_shader_unload(SuicTerrainShader* ts) {
if (ts && ts->shaderId != 0) {
Shader s = {ts->shaderId, NULL};
UnloadShader(s);
ts->shaderId = 0;
if (ts && ts->shader.id != 0) {
UnloadShader(ts->shader);
ts->shader.id = 0;
}
}
void suic_terrain_shader_set_uniforms(SuicTerrainShader* ts, SuicVec3 viewPos,
SuicVec3 lightDir, SuicVec3 lightColor,
float intensity, float ambient) {
if (!ts || ts->shaderId == 0) return;
if (!ts || ts->shader.id == 0) return;
Shader shader = {ts->shaderId, NULL};
float viewPosArray[3] = {viewPos.x, viewPos.y, viewPos.z};
float lightDirArray[3] = {-lightDir.x, -lightDir.y, -lightDir.z};
float lightColorArray[3] = {lightColor.x, lightColor.y, lightColor.z};
float terrainColorArray[3] = {80.0f / 255.0f, 140.0f / 255.0f, 70.0f / 255.0f};
SetShaderValue(shader, ts->locViewPos, viewPosArray, SHADER_UNIFORM_VEC3);
SetShaderValue(shader, ts->locLightDir, lightDirArray, SHADER_UNIFORM_VEC3);
SetShaderValue(shader, ts->locLightColor, lightColorArray, SHADER_UNIFORM_VEC3);
SetShaderValue(shader, ts->locLightIntensity, &intensity, SHADER_UNIFORM_FLOAT);
SetShaderValue(shader, ts->locAmbientIntensity, &ambient, SHADER_UNIFORM_FLOAT);
SetShaderValue(shader, ts->locTerrainColor, terrainColorArray, SHADER_UNIFORM_VEC3);
SetShaderValue(ts->shader, ts->locViewPos, viewPosArray, SHADER_UNIFORM_VEC3);
SetShaderValue(ts->shader, ts->locLightDir, lightDirArray, SHADER_UNIFORM_VEC3);
SetShaderValue(ts->shader, ts->locLightColor, lightColorArray, SHADER_UNIFORM_VEC3);
SetShaderValue(ts->shader, ts->locLightIntensity, &intensity, SHADER_UNIFORM_FLOAT);
SetShaderValue(ts->shader, ts->locAmbientIntensity, &ambient, SHADER_UNIFORM_FLOAT);
SetShaderValue(ts->shader, ts->locTerrainColor, terrainColorArray, SHADER_UNIFORM_VEC3);
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "suic_math.h"
#include "raylib.h"
#include <stdbool.h>
/**
@ -27,7 +28,7 @@ void suic_terrain_cleanup(void);
/* Shader management */
typedef struct SuicTerrainShader {
unsigned int shaderId;
Shader shader;
int locViewPos;
int locLightDir;
int locLightColor;

View file

@ -1,7 +1,7 @@
gcc -O3 client.c -o client -lm -lraylib
./build_client.sh
gcc -O3 server.c -o server -lm -lraylib -lode
./server &
server_pid=$!
./client
./game_client
kill $server_pid

View file

@ -1,21 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "../libsuicmez/suicmez_gc.h"
void* gc_alloc(const TypeInfo* type, size_t size);
void gc_init(void);
void gc_shutdown(void);
// Helper for allocating arrays
void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) {
void* ptr = gc_alloc(type, elem_size * len);
if (init_data) memcpy(ptr, init_data, elem_size * len);
return ptr;
}
// Helper for allocating structs
void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) {
void* ptr = gc_alloc(type, size);
if (init_data) memcpy(ptr, init_data, size);
return ptr;
}

Binary file not shown.

Binary file not shown.

View file

@ -1,25 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
/* ---- Lifecycle ---- */
void suic_runtime_init(void);
void suic_runtime_shutdown(void);
/* ---- Window / Loop ---- */
void suic_app_init(int width, int height, const char* title);
bool suic_app_should_close(void);
void suic_app_frame_begin(void);
void suic_app_frame_end(void);
void suic_app_shutdown(void);
/* ---- Drawing ---- */
void suic_draw_hello(void);
/* ---- Gameplay helpers ---- */
const char* suic_item_name(uint8_t t);
struct Color* suic_apply_directional_lighting(
struct Color* baseColor,
struct Vector3* normal,
struct DirectionalLight* light
);

View file

@ -1,19 +0,0 @@
fn main() -> int do
suic_runtime_init()
defer suic_runtime_shutdown()
# Initialize application / window
suic_app_init(1280, 720, "Voxel Shooter - Client")
defer suic_app_shutdown()
# Main loop
while not suic_app_should_close() do
suic_app_frame_begin()
suic_draw_hello()
suic_app_frame_end()
end
0
end

Binary file not shown.

View file

@ -1,150 +0,0 @@
#include "suic_lighting.h"
#include "suic_terrain.h"
#include <math.h>
/* ===== LIGHT CREATION ===== */
SuicDirectionalLight suic_light_create(float dir_x, float dir_y, float dir_z,
float intensity, float ambient) {
SuicDirectionalLight light = {0};
light.direction = suic_v3_norm(suic_v3(dir_x, dir_y, dir_z));
light.color = suic_v3(1.0f, 1.0f, 1.0f);
light.intensity = intensity;
light.ambientIntensity = ambient;
light.shadowBias = 0.005f;
light.shadowIntensity = 0.4f;
return light;
}
SuicDirectionalLight suic_light_create_default(void) {
return suic_light_create(-0.8f, -1.0f, -0.6f, 1.2f, 0.3f);
}
/* ===== BASIC LIGHTING ===== */
SuicColor suic_light_apply(SuicColor base, SuicVec3 normal, SuicDirectionalLight* light) {
SuicVec3 lightDir = light->direction;
/* Diffuse: Lambertian */
float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y +
lightDir.z * normal.z));
float brightness = light->ambientIntensity +
(diff * light->intensity * (1.0f - light->ambientIntensity));
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
return (SuicColor){r, g, b, base.a};
}
/* ===== SHADOW CALCULATIONS ===== */
float suic_shadow_factor(SuicVec3 worldPos, SuicVec3 lightDir, float maxDist) {
float distFromLight = fmaxf(0.0f, worldPos.y - 2.0f);
float shadowIntensity = fmaxf(0.0f, 1.0f - (distFromLight / maxDist));
return 1.0f - (shadowIntensity * 0.15f);
}
float suic_shadow_temporal(SuicVec3 worldPos, float timePhase) {
float noiseVal = sinf(worldPos.x * 0.5f + timePhase) *
cosf(worldPos.z * 0.5f + timePhase);
return 1.0f + (noiseVal * 0.02f);
}
/* ===== LIGHTING WITH SHADOWS ===== */
SuicColor suic_light_apply_shadows(SuicColor base, SuicVec3 normal, SuicVec3 worldPos,
SuicDirectionalLight* light) {
float shadowFactor = suic_shadow_factor(worldPos, light->direction, 10.0f);
float adjustedIntensity = light->intensity * shadowFactor;
SuicVec3 lightDir = light->direction;
float diff = fmaxf(0.0f, -(lightDir.x * normal.x + lightDir.y * normal.y +
lightDir.z * normal.z));
float brightness = light->ambientIntensity +
(diff * adjustedIntensity * (1.0f - light->ambientIntensity));
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
return (SuicColor){r, g, b, base.a};
}
/* ===== AMBIENT OCCLUSION ===== */
float suic_ao_calculate(SuicVec3 worldPos, SuicVec3 normal) {
float sampleRadius = 3.0f;
float aoAccum = 0.0f;
int numSamples = 8;
float centerHeight = worldPos.y;
float pi = 3.14159265f;
for (int i = 0; i < numSamples; i++) {
float angle = (2.0f * pi * (float)i) / (float)numSamples;
float sx = worldPos.x + cosf(angle) * sampleRadius;
float sz = worldPos.z + sinf(angle) * sampleRadius;
float sh = suic_terrain_height(sx, sz);
float heightDiff = sh - centerHeight;
if (heightDiff > 0.1f) {
float aoAmount = fminf(1.0f, heightDiff / 2.0f);
aoAccum += aoAmount;
}
}
float aoFactor = 1.0f - (aoAccum / (float)numSamples) * 0.6f;
return fmaxf(0.2f, aoFactor);
}
/* ===== PHONG LIGHTING ===== */
SuicColor suic_light_apply_phong(SuicColor base, SuicVec3 normal, SuicVec3 worldPos,
SuicVec3 camPos, SuicDirectionalLight* light) {
SuicVec3 lightDir = suic_v3_norm(light->direction);
SuicVec3 normal_n = suic_v3_norm(normal);
SuicVec3 viewDir = suic_v3_norm(suic_v3_sub(camPos, worldPos));
/* Diffuse */
float diffuse = fmaxf(0.0f, -suic_v3_dot(lightDir, normal_n));
/* Specular: Blinn-Phong */
SuicVec3 negLight = suic_v3(-lightDir.x, -lightDir.y, -lightDir.z);
SuicVec3 halfVec = suic_v3_norm(suic_v3_add(suic_v3_norm(negLight), viewDir));
float specular = powf(fmaxf(0.0f, suic_v3_dot(halfVec, normal_n)), 32.0f) * 0.5f;
/* Ambient occlusion */
float ao = suic_ao_calculate(worldPos, normal_n);
/* Shadow */
float shadowFactor = suic_shadow_factor(worldPos, light->direction, 10.0f);
/* Combine */
float brightness = light->ambientIntensity * ao;
brightness += diffuse * light->intensity * shadowFactor *
(1.0f - light->ambientIntensity) * ao;
brightness += specular * light->intensity * shadowFactor * 0.6f;
brightness = fminf(1.0f, brightness);
int r = (int)(base.r * brightness);
int g = (int)(base.g * brightness);
int b = (int)(base.b * brightness);
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
return (SuicColor){r, g, b, base.a};
}

Binary file not shown.