470 lines
22 KiB
Markdown
470 lines
22 KiB
Markdown
# Генератор мира
|
||
|
||
## Содержание
|
||
|
||
- [Основные понятия](#основные-понятия)
|
||
- [Глобальные переменные](#глобальные-переменные)
|
||
- [Файл конфигурации](#файл-конфигурации)
|
||
- [Фрагменты](#фрагменты)
|
||
- [Структуры](#структуры)
|
||
- [Биомы](#биомы)
|
||
* [Параметры биомов](#параметры-биомов)
|
||
* [Выбор биома](#выбор-биома)
|
||
- [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-фрагмент)
|
||
- [Генерация карты высот](#генерация-карты-высот)
|
||
- [Ручная расстановка структур](#ручная-расстановка-структур)
|
||
* [Размещения структур/тоннелей](#размещения-структуртоннелей)
|
||
* [Расстановка малых структур](#расстановка-малых-структур)
|
||
* [Расстановка 'широких' структур](#расстановка-широких-структур)
|
||
- [Структурный воздух](#структурный-воздух)
|
||
|
||
## Основные понятия
|
||
|
||
Понятия используемые далее по тексту.
|
||
|
||
- **Комбинируемый массив/объект** - 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** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
|
||
|
||
## Глобальные переменные
|
||
|
||
В скрипте генератора доступны следующие переменные:
|
||
|
||
- `SEED` - зерно генерации мира
|
||
- `__DIR__` - директория генератора (`пак:generators/имя_генератора.files/`)
|
||
- `__FILE__` - файл скрипта (`пак:generators/имя_генератора.files/script.lua`)
|
||
|
||
## Фрагменты
|
||
|
||
Фрагмент является сохраненной для дальнейшего использования, областью мира, как и чанк, ограниченную некоторой шириной, высотой и длиной. Фрагмент может содержать данные не только о блоках, попадающих в область, но и о инвентарях блоков области, а так же сущностях. В отличие от чанка, размер фрагмента произволен.
|
||
|
||
На данный момент, фрагмент может быть создан при помощи команды `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, 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.
|
||
|
||

|
||
|
||
### heightmap:cellnoise(...)
|
||
|
||
Аналог heightmap:noise генерирующий клеточный шум.
|
||
|
||
Зерно шума может быть указано в поле `map.noiseSeed`.
|
||
|
||

|
||
|
||
### heightmap:resize(...)
|
||
|
||
```lua
|
||
map:resize(ширина, высота, интерполяция)
|
||
```
|
||
|
||
Изменяет размер карты высот.
|
||
|
||
Доступные режимы интерполяции:
|
||
- 'nearest' - без интерполяции
|
||
- 'linear' - билинейная интерполяция
|
||
|
||
### 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`.
|
||
|
||
Фрагмент может быть обрезан до размеров содержимого (воздух игнорируется) вызовом метода `fragment:crop()`.
|
||
|
||
## Генерация карты высот
|
||
|
||
По-умолчанию, движок генерирует карту высот, состоящую из нулей.
|
||
|
||
Для генерации пользовательских карт высот требуется реализовать функцию:
|
||
```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
|
||
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">
|