VoxelEngine/doc/ru/world-generator.md
2025-11-24 21:24:30 +03:00

518 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Генератор мира
## Содержание
- [Основные понятия](#основные-понятия)
- [Глобальные переменные](#глобальные-переменные)
- [Файл конфигурации](#файл-конфигурации)
- [Фрагменты](#фрагменты)
- [Структуры](#структуры)
- [Биомы](#биомы)
* [Параметры биомов](#параметры-биомов)
* [Выбор биома](#выбор-биома)
- [Heightmap (карта высот)](#heightmap-карта-высот)
* [Конструктор](#конструктор)
* [Унарные операции](#унарные-операции)
* [Бинарные операции](#бинарные-операции)
* [heightmap:dump(...)](#heightmapdump)
* [heightmap:noise(...)](#heightmapnoise)
* [heightmap:cellnoise(...)](#heightmapcellnoise)
* [heightmap:resize(...)](#heightmapresize)
* [heightmap:crop(...)](#heightmapcrop)
* [heightmap:at(x, y)](#heightmapatx-y)
- [VoxelFragment (фрагмент)](#voxelfragment-фрагмент)
- [Генерация карты высот](#генерация-карты-высот)
- [Ручная расстановка структур](#ручная-расстановка-структур)
* [Размещения структур/тоннелей](#размещения-структуртоннелей)
* [Расстановка малых структур](#расстановка-малых-структур)
* [Расстановка 'широких' структур](#расстановка-широких-структур)
- [Структурный воздух](#структурный-воздух)
- [Генератор 'Demo' (base:demo)](#генератор-demo-basedemo)
## Основные понятия
Понятия используемые далее по тексту.
- **Комбинируемый массив/объект** - 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** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4.
- **wide-structs-chunks-radius** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
- **heightmap-inputs** - массив номеров карт параметров, которые будут переданы таблицей inputs в функцию генерации карты высот.
## Глобальные переменные
В скрипте генератора доступны следующие переменные:
- `SEED` - зерно генерации мира
- `__DIR__` - директория генератора (`пак:generators/имя_генератора.files/`)
- `__FILE__` - файл скрипта (`пак:generators/имя_генератора.files/script.lua`)
## Фрагменты
Фрагмент является сохраненной для дальнейшего использования, областью мира, как и чанк, ограниченную некоторой шириной, высотой и длиной. Фрагмент может содержать данные не только о блоках, попадающих в область, но и о инвентарях блоков области, а так же сущностях. В отличие от чанка, размер фрагмента произволен.
На данный момент, фрагмент может быть создан при помощи команды `fragment.save`, либо через функцию `generation.create_fragment`.
Фрагменты, используемые генератором, должны находиться в директории:
`generators/имя_генератора.files/fragments/`
## Структуры
Структура - набор правил по вставке фрагмента в мир генератором. Структуры объявляются в виде объектов в файле `generators/имя_генератора.files/structures.toml`. Пример:
```toml
tree0 = {}
tree1 = {}
tree2 = {}
tower = {lowering=-2}
coal_ore0 = {}
```
На данный момент, имя структуры должно совпадать с именем использованного фрагмента.
Доступные свойства:
- lowering - глубина погружения структуры под поверхность.
## Биомы
Биом определяет то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур.
Биомы объявляются в комбинируемом объекте:
`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.toml`.
- weight - вес, напрямую влияющий на шанс выбора конкретной структуры.
### Параметры биомов
Параметр генератора `biome-parameters` определяет количество параметров биомов, используемых при их выборе (примеры: температура, влажность).
Карты значений параметров биомов генеруются так же, как и карты высот.
Требуется реализовать функцию:
```lua
-- x, y - позиция начала карты (в точках)
-- w, h - ширина и высота карты (в точках)
-- bpd - (blocks per dot) число блоков на точку (масштаб)
function generate_biome_parameters(x, y, w, h, bpd)
-- создание карт высот (Heightmap) для каждого параметра биомов
-- ...
return картыерез_запятую
end
-- пример
function generate_biome_parameters(x, y, w, h, 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(ширина, высота)
```
### Унарные операции
Операции применяются ко всем значениям высоты.
```lua
map:abs()
```
Приводит значения высот к абсолютным.
### Бинарные операции
Операции с применением второй карты или скаляра.
Арифметические операции:
```lua
-- Прибавление
map:add(value: Heightmap|number)
-- Вычитание
map:sub(value: Heightmap|number)
-- Умножение
map:mul(value: Heightmap|number)
-- Возведение в степень
map:pow(value: Heightmap|number)
```
Другие операции:
```lua
-- Минимум
map:min(value: Heightmap|number)
-- Максимум
map:max(value: Heightmap|number)
-- Примешивание
map:mixin(value: Heightmap|number, t: Heightmap|number)
-- t - фактор смешивания от 0.0 до 1.0
-- смешивание производится по формуле:
-- map_value * (1.0 - t) + value * t
```
### 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.gif)
### heightmap:cellnoise(...)
Аналог heightmap:noise генерирующий клеточный шум.
Зерно шума может быть указано в поле `map.noiseSeed`.
![image](../images/cell-noise.gif)
### heightmap:resize(...)
```lua
map:resize(ширина, высота, интерполяция)
```
Изменяет размер карты высот.
Доступные режимы интерполяции:
- 'nearest' - без интерполяции
- 'linear' - билинейная интерполяция
- 'cubic' - бикубическая интерполяция
### heightmap:crop(...)
```lua
map:crop(x, y, ширина, высота)
```
Обрезает карту высот до заданной области.
### heightmap:at(x, y)
```lua
map:at(x, y) --> number
```
Возвращает значение высота на заданной позиции.
## 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`.
### Методы
```lua
-- Обрезает фрагмент до размеров содержимого
fragment:crop()
-- Устанавливает фрагмент в мир на указанной позиции
fragment:place(position: vec3, [опционально] rotation:int=0)
```
## Генерация карты высот
По-умолчанию, движок генерирует карту высот, состоящую из нулей.
Для генерации пользовательских карт высот требуется реализовать функцию:
```lua
function generate_heightmap(
x, y, -- смещение карты высот
w, h, -- размер карты высот, ожидаемый движком
bpd, -- число блоков на точку карты (blocks per dot) - масштаб
[опционально] inputs -- массив входных карт параметров биомов
-- (см. свойство heightmap-inputs генератора)
) --> Heightmap
```
Пример генерации карты высот из простого симплекс-шума с приведением
к нужному диапазону:
```lua
function generate_heightmap(x, y, w, h, bpd)
-- создаем карту высот с заданным размером
local map = Heightmap(w, h)
-- настраиваем зерно шума
map.noiseSeed = SEED
-- шум с масштабом 1/10 на 4 октавы с амплитудой 0.5
map:noise({x, y}, 0.1*bpd, 4, 0.5)
-- сдвигаем высоты к положительному диапазону
map:add(0.5)
return map
end
```
## Ручная расстановка структур
### Размещения структур/тоннелей
Размещение структуры / линии представляет собой массив из заданного
набора параметров.
Структура:
```lua
{имя_структуры, позиция_структуры, поворот, [опционально] приоритет}
```
Где:
- имя_структуры - строка содержащая имя структуры, зарегистрированная в structures.toml.
- позиция_структуры - vec3 (массив из трех чисел) относительно позиции чанка.
- поворот - число от 0 до 3 обозначающая поворот структуры по оси Y.
- приоритет - число определяющее порядок установки структур. Структуры с меньшем приоритетом перекрываются структурами с большим.
Тоннель:
```lua
{":line", блокаполнитель, точка_а, точка_б, радиус}
```
Где:
- блок_заполнитель - числовой id блока, из которого будет состоять структура.
- точка_а, точка_б - vec3, vec3 позиции начала и конца тоннеля.
- радиус - радиус тоннеля в блоках
Одиночный блок:
```lua
{":block", id_блока, позиция, [поворот], [приоритет]}
```
Где:
- id_блока: числовой runtimeid блока, который нужно поставить.
- позиция: vec3 позиция в блоках относительно начала текущего чанка.
- поворот: 03, поворот вокруг оси Y. По умолчанию: 0. Для расширенных блоков (размер > 1) этот поворот применяется ко всем сегментам.
- приоритет: целое число. Бóльший приоритет ставится позже и перезаписывает более низкий.
Примечания:
- `:block` автоматически раскладывает расширенные блоки на сегменты и заменяет любые блоки в занимаемых ячейках.
- Размещение корректно работает на границах чанков: движок сам разносит плейсмент по затрагиваемым прототипам на основе размера/AABB блока.
- `:block` используйте для точечных блоков; `:line` — для туннелей/линий.
### Расстановка малых структур
```lua
function place_structures(
x, z, -- позиция начала области в блоках
w, d, -- размер области в блоках
heights, -- карта высот чанка
chunk_height, -- высота чанка
) --> массив размещений структур
```
Структуры могут размещаться за пределами чанка, но не дальше, чем на один чанк.
Пример:
```lua
function place_structures(x, z, w, d, hmap, chunk_height)
local placements = {}
local height = hmap:at(w/2, h/2) * chunk_height
-- устанавливает башню по центру чанка
table.insert(placements, {
'tower', {w/2, height, d/2}, math.random() * 4, 2
})
return placements
end
```
### Расстановка 'широких' структур
Структуры и тоннели могут размещаться за пределами чанка, но не дальше, чем на число чанков, указанное в свойстве генератора `wide-structs-chunks-radius`.
В отличие от прошлой функции, сюда не передается карта высот,
так как вызов происходит на ранних этапах генерации чанка.
```lua
function place_structures_wide(
x, z, -- позиция начала области в блоках
w, d, -- размер области в блоках
chunk_height, -- высота чанка
) --> массив размещений структур / тоннелей
```
## Структурный воздух
`core:struct_air` - блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
# Генератор 'Demo' (base:demo)
## Добавление новой руды
Чтобы добавить новую руду из своего пака:
1. В папке `generators` создайте папку `demo.files` (demo.toml создавать не нужно).
2. В созданной папке создайте папку fragments и поместите в неё файл фрагмента руды.
3. В `demo.files` создайте файл structures.toml:
```toml
имя_фрагмента = {}
```
4. Также в `demo.files` создайте файл ores.json:
```json
[
{"struct": "имя_фрагмента", "rarity": редкость}
]
```
Чем выше значение редкости, тем меньше вероятность генерации руды.
Опираться можно на редкость угольной руды: 4400.