285 lines
12 KiB
C
285 lines
12 KiB
C
#include "suic_terrain.h"
|
|
#include "raylib.h"
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
/* ===== SIMPLEX NOISE IMPLEMENTATION ===== */
|
|
|
|
static const uint8_t perm[512] = {
|
|
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7,
|
|
225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190,
|
|
6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117,
|
|
35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136,
|
|
171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158,
|
|
231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46,
|
|
245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209,
|
|
76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86,
|
|
164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5,
|
|
202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16,
|
|
58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44,
|
|
154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253,
|
|
19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97,
|
|
228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51,
|
|
145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
|
|
84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
|
|
222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156,
|
|
180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233,
|
|
7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
|
|
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203,
|
|
117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125,
|
|
136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146,
|
|
158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55,
|
|
46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73,
|
|
209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159,
|
|
86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
|
|
5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47,
|
|
16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2,
|
|
44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39,
|
|
253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246,
|
|
97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81,
|
|
51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
|
|
184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
|
|
93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
|
|
156, 180};
|
|
|
|
static float grad2(int hash, float x, float y) {
|
|
int h = hash & 7;
|
|
float u = h < 4 ? x : y;
|
|
float v = h < 4 ? y : x;
|
|
return ((h & 1) ? -u : u) + ((h & 2) ? -2.0f * v : 2.0f * v);
|
|
}
|
|
|
|
float suic_simplex_noise_2d(float x, float y) {
|
|
const float F2 = 0.366025403f;
|
|
const float G2 = 0.211324865f;
|
|
float s = (x + y) * F2;
|
|
int i = (int)floorf(x + s);
|
|
int j = (int)floorf(y + s);
|
|
float t = (i + j) * G2;
|
|
float X0 = i - t, Y0 = j - t;
|
|
float x0 = x - X0, y0 = y - Y0;
|
|
int i1 = (x0 > y0) ? 1 : 0;
|
|
int j1 = (x0 > y0) ? 0 : 1;
|
|
float x1 = x0 - i1 + G2, y1 = y0 - j1 + G2;
|
|
float x2 = x0 - 1.0f + 2.0f * G2, y2 = y0 - 1.0f + 2.0f * G2;
|
|
int ii = i & 255, jj = j & 255;
|
|
float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f;
|
|
float t0 = 0.5f - x0 * x0 - y0 * y0;
|
|
if (t0 >= 0.0f) {
|
|
t0 *= t0;
|
|
n0 = t0 * t0 * grad2(perm[ii + perm[jj]], x0, y0);
|
|
}
|
|
float t1 = 0.5f - x1 * x1 - y1 * y1;
|
|
if (t1 >= 0.0f) {
|
|
t1 *= t1;
|
|
n1 = t1 * t1 * grad2(perm[ii + i1 + perm[jj + j1]], x1, y1);
|
|
}
|
|
float t2 = 0.5f - x2 * x2 - y2 * y2;
|
|
if (t2 >= 0.0f) {
|
|
t2 *= t2;
|
|
n2 = t2 * t2 * grad2(perm[ii + 1 + perm[jj + 1]], x2, y2);
|
|
}
|
|
return 45.0f * (n0 + n1 + n2);
|
|
}
|
|
|
|
float suic_fbm_noise(float x, float y, int octaves) {
|
|
float value = 0.0f, amplitude = 1.0f, frequency = 1.0f, max_value = 0.0f;
|
|
for (int i = 0; i < octaves; i++) {
|
|
value += suic_simplex_noise_2d(x * frequency, y * frequency) * amplitude;
|
|
max_value += amplitude;
|
|
amplitude *= 0.5f;
|
|
frequency *= 2.0f;
|
|
}
|
|
return value / max_value;
|
|
}
|
|
|
|
float suic_terrain_height(float x, float z) {
|
|
float height = suic_fbm_noise(x * 0.05f, z * 0.05f, 2);
|
|
height += suic_fbm_noise(x * 0.01f, z * 0.01f, 2) * 1.5f;
|
|
return height * 4.0f + 2.0f;
|
|
}
|
|
|
|
/* ===== TERRAIN MESH & MODEL ===== */
|
|
|
|
static Mesh g_terrainMesh = {0};
|
|
static Model g_terrainModel = {0};
|
|
static SuicTerrainShader g_terrainShader = {0};
|
|
static bool g_terrainInitialized = false;
|
|
|
|
static Mesh generate_terrain_mesh(void) {
|
|
int size = SUIC_TERRAIN_SIZE;
|
|
Mesh mesh = {0};
|
|
|
|
int vertexCount = size * size;
|
|
int triangleCount = (size - 1) * (size - 1) * 2;
|
|
|
|
mesh.vertexCount = vertexCount;
|
|
mesh.triangleCount = triangleCount;
|
|
|
|
mesh.vertices = (float *)MemAlloc(vertexCount * 3 * sizeof(float));
|
|
mesh.texcoords = (float *)MemAlloc(vertexCount * 2 * sizeof(float));
|
|
mesh.normals = (float *)MemAlloc(vertexCount * 3 * sizeof(float));
|
|
mesh.indices = (unsigned short *)MemAlloc(triangleCount * 3 * sizeof(unsigned short));
|
|
|
|
/* Generate vertices */
|
|
for (int z = 0; z < size; z++) {
|
|
for (int x = 0; x < size; x++) {
|
|
int idx = z * size + x;
|
|
float wx = ((float)x - size / 2.0f) * SUIC_TERRAIN_SCALE;
|
|
float wz = ((float)z - size / 2.0f) * SUIC_TERRAIN_SCALE;
|
|
float wy = suic_terrain_height(wx, wz);
|
|
|
|
mesh.vertices[idx * 3 + 0] = wx;
|
|
mesh.vertices[idx * 3 + 1] = wy;
|
|
mesh.vertices[idx * 3 + 2] = wz;
|
|
|
|
mesh.texcoords[idx * 2 + 0] = (float)x / (float)(size - 1);
|
|
mesh.texcoords[idx * 2 + 1] = (float)z / (float)(size - 1);
|
|
}
|
|
}
|
|
|
|
/* Generate indices */
|
|
int triIdx = 0;
|
|
for (int z = 0; z < size - 1; z++) {
|
|
for (int x = 0; x < size - 1; x++) {
|
|
int i0 = z * size + x;
|
|
int i1 = z * size + (x + 1);
|
|
int i2 = (z + 1) * size + x;
|
|
int i3 = (z + 1) * size + (x + 1);
|
|
|
|
mesh.indices[triIdx * 3 + 0] = i0;
|
|
mesh.indices[triIdx * 3 + 1] = i2;
|
|
mesh.indices[triIdx * 3 + 2] = i1;
|
|
triIdx++;
|
|
|
|
mesh.indices[triIdx * 3 + 0] = i1;
|
|
mesh.indices[triIdx * 3 + 1] = i2;
|
|
mesh.indices[triIdx * 3 + 2] = i3;
|
|
triIdx++;
|
|
}
|
|
}
|
|
|
|
/* Calculate normals using height field gradient */
|
|
for (int z = 0; z < size; z++) {
|
|
for (int x = 0; x < size; x++) {
|
|
int idx = z * size + x;
|
|
|
|
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 dh_dx = (h_right - h_left) / (2.0f * SUIC_TERRAIN_SCALE);
|
|
float dh_dz = (h_down - h_up) / (2.0f * SUIC_TERRAIN_SCALE);
|
|
|
|
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;
|
|
mesh.normals[idx * 3 + 1] = ny / len;
|
|
mesh.normals[idx * 3 + 2] = nz / len;
|
|
} else {
|
|
mesh.normals[idx * 3 + 0] = 0;
|
|
mesh.normals[idx * 3 + 1] = 1;
|
|
mesh.normals[idx * 3 + 2] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
UploadMesh(&mesh, false);
|
|
return mesh;
|
|
}
|
|
|
|
void suic_terrain_init(void) {
|
|
if (g_terrainInitialized) return;
|
|
|
|
g_terrainMesh = generate_terrain_mesh();
|
|
g_terrainModel = LoadModelFromMesh(g_terrainMesh);
|
|
g_terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){80, 140, 70, 255};
|
|
|
|
g_terrainShader = suic_terrain_shader_load();
|
|
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;
|
|
}
|
|
|
|
void suic_terrain_draw(SuicVec3 cameraPos) {
|
|
if (!g_terrainInitialized) return;
|
|
|
|
/* Set shader uniforms if shader is loaded */
|
|
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);
|
|
}
|
|
|
|
g_terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = (Color){80, 140, 70, 255};
|
|
DrawModel(g_terrainModel, (Vector3){0, 0, 0}, 1.0f, WHITE);
|
|
}
|
|
|
|
void suic_terrain_cleanup(void) {
|
|
if (!g_terrainInitialized) return;
|
|
UnloadModel(g_terrainModel);
|
|
suic_terrain_shader_unload(&g_terrainShader);
|
|
g_terrainInitialized = false;
|
|
}
|
|
|
|
/* ===== SHADER MANAGEMENT ===== */
|
|
|
|
SuicTerrainShader suic_terrain_shader_load(void) {
|
|
SuicTerrainShader ts = {0};
|
|
ts.shader = LoadShader("game/terrain.vs", "game/terrain.fs");
|
|
|
|
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->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->shader.id == 0) return;
|
|
|
|
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(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);
|
|
}
|