From fe42371c8c5b80b174372de846fafb7590fc3ff1 Mon Sep 17 00:00:00 2001 From: Orangerot Date: Thu, 5 Jun 2025 03:43:25 +0200 Subject: refactor: move terrain generation and drawing to own file --- world.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 world.c (limited to 'world.c') diff --git a/world.c b/world.c new file mode 100644 index 0000000..3b7c6ba --- /dev/null +++ b/world.c @@ -0,0 +1,181 @@ +/* + * Tux-Town is a chill life-simulation game. + * Copyright (C) 2025 orangerot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "assets.h" +#include "world.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define CLAMP2(val, min, max) MIN(max, MAX(min, val)) +#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) +#define LROT(v,n) ((v << n) | (v >> (sizeof(v)*8 - n))) +#define RROT(v,n) ((v >> n) | (v << (sizeof(v)*8 - n))) + +#define MAP_SIZE 64 + +struct ModelDirection { + enum Asset asset; + unsigned char pattern; +}; + +struct ModelDirection rivers[] = { + /* 0b12345678 + * 1 | 2 | 3 + * 8 | | 4 + * 7 | 6 | 5 + */ + { .pattern = 0b11111111, .asset = ground_riverOpen }, + // edge + { .pattern = 0b11011111, .asset = ground_riverCornerSmall }, + { .pattern = 0b01011111, .asset = ground_riverSideOpen }, + { .pattern = 0b11110001, .asset = ground_riverSide }, + { .pattern = 0b01010101, .asset = ground_riverCross }, + { .pattern = 0b01010001, .asset = ground_riverSplit }, + // STRAIGHT + { .pattern = 0b01000100, .asset = ground_riverStraight }, + // corner + { .pattern = 0b11000001, .asset = ground_riverCorner }, + // L SHAPE + { .pattern = 0b01000001, .asset = ground_riverBend }, + // closed + { .pattern = 0b01000000, .asset = ground_riverEndClosed }, + { .pattern = 0b00000000, .asset = ground_riverTile }, +}; + +void generate_river(struct World *world, int previous) { + Color *map_data = world->map_data; + int map_size = world->size; + + int x = previous % map_size, y = previous / map_size; + if (x == 0 || x == map_size -1 || y == 0 || y == map_size -1) return; + + int local_minimum_map_i = previous; + int local_minimum_val = 255; + int gradients[4][2] = {{0,-1},{-1,0},{0,1},{1,0}}; + for (int gradient_i = 0; gradient_i < 4; gradient_i++) { + int dx = CLAMP(x + gradients[gradient_i][0], 0, map_size - 1); + int dy = CLAMP(y + gradients[gradient_i][1], 0, map_size - 1); + int i = dy * map_size + dx; + if (i == previous || map_data[i].r == 1) continue; + if (map_data[i].b < local_minimum_val) { + local_minimum_map_i = i; + local_minimum_val = map_data[i].b; + } + } + if (local_minimum_val == 255) return; + map_data[local_minimum_map_i].r = 1; + generate_river(world, local_minimum_map_i); +} + +void select_river_tile(struct World *world, int x, int y, size_t *river_i, size_t *direction) { + Color *map_data = world->map_data; + int map_size = world->size; + + int surrounding[8][2] = {{-1,-1},{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0}}; + unsigned char river_tile = 0; + for (int surrounding_i = 0; surrounding_i < 8; surrounding_i++) { + int dx = CLAMP(x + surrounding[surrounding_i][0], 0, map_size - 1); + int dy = CLAMP(y + surrounding[surrounding_i][1], 0, map_size - 1); + if (map_data[dy * map_size + dx].r) + river_tile |= 1 << (7 - surrounding_i); + } + for (*river_i = 0; *river_i < 11; (*river_i)++) { + for (*direction = 0; *direction < 4; (*direction)++) { + if ((rivers[*river_i].pattern & RROT(river_tile, 2 * *direction)) == rivers[*river_i].pattern) { + return; + } + } + } +} + + +void gen_terrain(struct World *world) { + int map_size = world->size; + size_t global_minimum_map_i; + size_t global_minimum_val = 255; + + world->map = GenImagePerlinNoise(map_size, map_size, 0, 0, 1.f); + world->map_texture = LoadTextureFromImage(world->map); + world->map_data = LoadImageColors(world->map); + + for (size_t i = 0; i < map_size * map_size; i++) { + int x = i % map_size, y = i / map_size; + Color c = world->map_data[i]; + if (c.r < global_minimum_val) { + global_minimum_map_i = i; + global_minimum_val = world->map_data[i].b; + } + world->map_data[i] = (Color) { + .r = 0, + .g = MAX(0, c.g - 64) / 32, + .b = c.b, + .a = 255 + }; + } + + world->map_data[global_minimum_map_i].r = 1; + generate_river(world, global_minimum_map_i); + generate_river(world, global_minimum_map_i); +} + +// void gen_room() { +// +// } +// +// void unload_world() {} + +void draw_world(struct World *world) { + int map_size = world->size; + Color *map_data = world->map_data; + Model wall = world->wall; + Model ground = world->floor; + + for (int i = 0; i < map_size * map_size; i++) { + int x = i % map_size, y = i / map_size; + int gradients[4][2] = {{0,-1},{-1,0},{0,1},{1,0}}; + for (int gradient_i = 0; gradient_i < 4; gradient_i++) { + int dx = CLAMP(x + gradients[gradient_i][0], 0, map_size - 1); + int dy = CLAMP(y + gradients[gradient_i][1], 0, map_size - 1); + for (int height = map_data[i].g; height < map_data[dy * map_size + dx].g; height++) { + DrawModelEx(wall, + (Vector3){ + .x = map_size * (x / (float) map_size - 0.5f), + .y = height, + .z = map_size * (y / (float) map_size - 0.5f) + }, + (Vector3) {0, 1, 0}, gradient_i * 90.f, (Vector3) {1,1,1}, WHITE); + } + } + size_t river_i, direction; + if (map_data[i].r) { + select_river_tile(world, x, y, &river_i, &direction); + } + DrawModelEx(map_data[i].r ? assets[rivers[river_i].asset] : ground, + (Vector3){ + .x = map_size * (x / (float) map_size - 0.5f), + .y = map_data[i].g, //- (map_gradient_magnitude_data[i].g < 2), + .z = map_size * (y / (float) map_size - 0.5f) + } , (Vector3) {0,1,0}, map_data[i].r ? direction * 90.f: 0, + (Vector3) {1,1,1}, + WHITE); + } +} + -- cgit v1.2.3