diff --git a/doc/images/simplex-noise.gif b/doc/images/simplex-noise.gif new file mode 100644 index 00000000..8eca11ae Binary files /dev/null and b/doc/images/simplex-noise.gif differ diff --git a/doc/ru/world-generator.md b/doc/ru/world-generator.md new file mode 100644 index 00000000..38e1fd92 --- /dev/null +++ b/doc/ru/world-generator.md @@ -0,0 +1,248 @@ +# Генератор мира + +## Основные понятия + +Понятия используемые далее по тексту. + +- **Комбинируемый массив/объект** - TOML или JSON файл, который комбинируется из нескольких версий в разных паках, что позволяет добавлять в него данные извне. Поля комбинированного объекта перезаписываются в порядке от первого к последнему, так же, как и другие ресурсы в паках. В случае комбинируемого массива, проверка на дубликаты **не** выполняется. +- **Биом** - информация, определяющая то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур. +- **Растение** - случайно расставляемый на поверхности блок. +- **Малая структура** - структура, размер которой не превышает размера чанка. Пример: деревья. + + +## Файл конфигурации + +Генератор мира распознается при наличии файла `generators/имя_генератора.toml`. Другие файлы, относящиеся к генератору, должны находиться в директории `generators/имя_генератора.files/`: +- biomes.toml - объявления биомов +- structures.toml - объявления структур +- script.lua - скрипт генератора +- fragments - директория в которой располагаются файлы фрагментов + +Основные свойства, описываемые в файле конфигурации: +- **caption** - отображаемое имя генератора. По-умолчанию генерируется из id. +- **biome-parameters** - количество параметров выбора биомов (от 0 до 4). По-умолчанию: 0. +- **sea-level** - уровень моря (ниже этого уровня вместо воздуха будут генерироваться слои моря (sea-layers)). По-умолчанию: 0. +- **biomes-bpd** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 8. +- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4. + +## Фрагменты + +Фрагмент является сохраненной для дальнейшего использования, областью мира, как и чанк, ограниченную некоторой шириной, высотой и длиной. Фрагмент может содержать данные не только о блоках, попадающих в область, но и о инвентарях блоков области, а так же сущностях. В отличие от чанка, размер фрагмента произволен. + +На данный момент, фрагмент может быть создан при помощи команды `fragment.save`, либо через функцию `generation.create_fragment`. + +Фрагменты, используемые генератором, должны находиться в директории: +`generators/имя_генератора.files/fragments/` + +## Структуры + +Структура - набор правил по вставке фрагмента в мир генератором. На данный момент не имеет свойств, создаваясь в виде пустых объектов в файле `generators/имя_генератора.files/structures.json`. Пример: +```lua +{ + "tree0": {}, + "tree1": {}, + "tree2": {}, + "tower": {}, + "coal_ore0": {} +} +``` + +На данный момент, имя структуры должно совпадать с именем использованного фрагмента. +## Биомы + +Биом определяет то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур. + +Биомы объявляются в комбинируемом объекте: +`generators/имя_генератора.files/biomes.toml` + +Разберем структуру биома на примере леса из генератора base:demo: +```toml +[forest] +parameters = [ + {weight=1, value=1}, + {weight=0.5, value=0.2} +] +layers = [ + {below-sea-level=false, height=1, block="base:grass_block"}, + {below-sea-level=false, height=7, block="base:dirt"}, + {height=-1, block="base:stone"}, + {height=1, block="base:bazalt"} +] +sea-layers = [ + {height=-1, block="base:water"} +] +plant-chance = 0.4 +plants = [ + {weight=1, block="base:grass"}, + {weight=0.03, block="base:flower"} +] +structure-chance = 0.032 +structures = [ + {name="tree0", weight=1}, + {name="tree1", weight=1}, + {name="tree2", weight=1}, + {name="tower", weight=0.002} +] +``` + +- ключ forest - имя биома +- parameters - веса и центральные значения параметров для биома. См. раздел [выбор биома](#выбор-биома). Количество записей должно соответствовать количеству параметров выбора биомов. +- layers - слои блоков от верхнего к нижнему. + - height - высота слоя в блоках. -1 используется для обозначения безразмерного (заполняющего) слоя, который может быть только один. Его высота вычисляется автоматически. + - block - полное имя блока + - below-sea-level - может ли слой быть сгенерированным ниже уровня моря (пример: дёрн). При значении false, при генерации ниже уровня моря, слой будет заменён на следующий. +- sea-layers - слои океана. Положение верхнего слоя совпадает с высотой уровня моря. +- plant-chance - вероятность генерации растения на блоке поверхности. +- plants - растения, случайно расставляемые на поверхности. + - weight - вес, напрямую влияющий на шанс выбора конкретного растения. + - block - блок растения +- structure-chance - вероятность генерации малой структуры на блоке поверхности. +- structures - структуры, случайно расставляемые на поверхности. + - name - имя структуры, объявленной в `structures.json`. + - weight - вес, напрямую влияющий на шанс выбора конкретной структуры. + +### Параметры биомов + +Параметр генератора `biome-parameters` определяет количество параметров биомов, используемых при их выборе (примеры: температура, влажность). + +Карты значений параметров биомов генеруются так же, как и карты высот. + +Требуется реализовать функцию: +```lua +-- x, y - позиция начала карты (в точках) +-- w, h - ширина и высота карты (в точках) +-- bpd - (blocks per dot) число блоков на точку (масштаб) +function generate_biome_parameters(x, y, w, h, seed, bpd) + -- создание карт высот (Heightmap) для каждого параметра биомов + -- ... + return карты_через_запятую +end + +-- пример +function generate_biome_parameters(x, y, w, h, seed, s) + -- карта температур + local tempmap = Heightmap(w, h) + tempmap.noiseSeed = seed + 5324 + tempmap:noise({x, y}, 0.04*s, 6) + tempmap:pow(3) + -- карта влажности + local hummap = Heightmap(w, h) + hummap.noiseSeed = seed + 953 + hummap:noise({x, y}, 0.04*s, 6) + hummap:pow(3) + + return tempmap, hummap +end +``` + +### Выбор биома + +После генерации карт параметров для каждого биома вычисляются оценки по всем параметрам: + +$score = \frac{|V - V_b|}{W_b}$ + +Где $V$ - значение параметра, $V_b$ центральное значение параметра для биома, $W_b$ - вес биома для параметра. + +Генератор выбирает биом с **наименьшей** суммой оценок для параметров. + + +>[!WARNING] +> При неудачной настройке значений и весов параметров у биомов может возникать эффект, имеющий ту же природу, что и конфликт глубины в 3D графике при наложении двух поверхностей. +> + В случае биомов, узор выглядит случайным из-за искривления этих 'поверхностей' шумом, используемым при генерации карт параметров. + > + Для избавления от эффекта можно либо скорректировать веса или значения параметров биомов, либо увеличить разницу в генерации карт параметров. + + +## Heightmap (карта высот) + +Heightmap это класс для работы с картами высот (матрицами чисел с плавающей точкой произвольного размера). + +### Конструктор + +Конструктор карты высот требует указания целочисленных ширины и высоты. + +```lua +local map = Heightmap(ширина, высота) +``` + +### heightmap:dump(...) + +Метод используемый для отладки, создает изображение на основе карты высот переводя значения из дипазона \[-1.0, 1.0\] в значения яркости \[0, 255\], сохраняя в указанный файл. + +```lua +map:dump('export:test.png') +``` + +### heightmap:noise(...) + +Метод генерирующий симплекс-шум, прибавляя его к имеющимся значениям. + +Зерно шума может быть указано в поле `map.noiseSeed`. + +```lua +map:noise( + -- смещение координат + offset: {number, number}, + -- коэфициент масштабирования координат + scale: number, + -- число октав шума (по-умолчанию: 1) + [опционально] octaves: integer, + -- множитель амплитуды шума (по-умолчанию: 1.0) + [опционально] multiplier: number, + -- карта смещений координаты X при генерации шума + [опционально] shiftMapX: Heightmap, + -- карта смещений координаты Y при генерации шума + [опционально] shiftMapY: Heightmap, +) -> nil +``` + +Визуализация шума с октавами 1, 2, 3, 4 и 5. + +![image](../images/simplex-noise.md) + +### heightmap:cellnoise(...) + +Аналог heightmap:noise генерирующий клеточный шум. + +Зерно шума может быть указано в поле `map.noiseSeed`. + +## VoxelFragment (фрагмент) + +Фрагмент создается вызовом функции: +```lua +generation.create_fragment( + -- точка A + a: vec3, + -- точка B + b: vec3, + -- автоматически обрезать фрагмент, если возможно + crop: bool +) -> VoxelFragment +``` + +Фрагмент может быть загружен из файла: +```lua +generation.load_fragment( + -- файл фрагмента + filename: str +) -> VoxelFragment +``` + +Фрагмент может быть сохранен в файл: +```lua +generation.save_fragment( + -- сохраняемый фрагмент + fragment: VoxelFragment, + -- файл + filename: str +) -> nil +``` + +Размер фрагмента доступен как свойство `size`. + +Фрагмент может быть обрезан до размеров содержимого (воздух игнорируется) вызовом метода `fragment:crop()`. + +## Структурный воздух + +`core:struct_air` - блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.