Merge pull request #309 from MihailRis/update-docs

Update docs
This commit is contained in:
MihailRis 2024-10-16 10:01:04 +03:00 committed by GitHub
commit 8187568e69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1451 additions and 611 deletions

View File

@ -1,16 +1,21 @@
# Documentation
Documentation for the in-development engine version 0.23.
[Documentation for the last stable version (0.22.x)](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.22/doc/en/main-page.md)
## Sections
- [Engine usage recommendations](engine-use-recommendations.md)
- [Content-packs](content-packs.md)
- [Block properties](block-properties.md)
- [Item properties](item-properties.md)
- [XML UI building](xml-ui-layouts.md)
- [Assets preloading](assets-preload.md)
- [Audio](audio.md)
- [Scripting](scripting.md)
- [Console](console.md)
- [Block models](block-models.md)
- [Rigging](rigging.md)
- [Block properties](block-properties.md)
- [Console](console.md)
- [Content-packs](content-packs.md)
- [Engine usage recommendations](engine-use-recommendations.md)
- [Item properties](item-properties.md)
- [Resources (resources.json)](resources.md)
- [Rigging](rigging.md)
- [Scripting](scripting.md)
- [World generator engine](world-generator.md)
- [XML UI building](xml-ui-layouts.md)

View File

@ -10,14 +10,19 @@ Subsections:
- [Entities and components](scripting/ecs.md)
- [Libraries](#)
- [block](scripting/builtins/libblock.md)
- [item](scripting/builtins/libitem.md)
- [entities](scripting/builtins/libentities.md)
- [cameras](scripting/builtins/libcameras.md)
- [entities](scripting/builtins/libentities.md)
- [gui](scripting/builtins/libgui.md)
- [hud](scripting/builtins/libhud.md)
- [inventory](scripting/builtins/libinventory.md)
- [item](scripting/builtins/libitem.md)
- [mat4](scripting/builtins/libmat4.md)
- [pack](scripting/builtins/libpack.md)
- [player](scripting/builtins/libplayer.md)
- [quat](scripting/builtins/libquat.md)
- [time](scripting/builtins/libtime.md)
- [vec2, vec3, vec4](scripting/builtins/libvecn.md)
- [world](scripting/builtins/libworld.md)
- [Module core:bit_converter](scripting/modules/core_bit_converter.md)
- [Module core:data_buffer](scripting/modules/core_data_buffer.md)
- [Module core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md)
@ -40,268 +45,3 @@ not part of Lua syntax.
require "packid:module_name" -- load Lua module from pack-folder/modules/
-- no extension included, just name
```
## *world* library
## Библиотека *world*
```python
world.get_list() -> tables array {
name: str,
icon: str
}
```
Retuns worlds information: name and preview/icon (loading automatically).
```python
world.get_day_time() -> number
```
Returns current day time in range \[0.0-1.0\] where 0.0 and 1.0 - midnight, 0.5 - noon.
```python
world.set_day_time(time: number)
```
Set day time value.
```python
world.set_day_time_speed(value: number)
```
Sets the specified speed for game time.
```python
world.get_day_time_speed() -> number
```
Returns the speed for game time.
```python
world.get_total_time() -> number
```
Returns total time passed in the world
```python
world.get_seed() -> int
```
Returns world seed.
``` python
world.is_day() -> boolean
```
Proves that this is the current time during the day. From 0.2(8 am) to 0.8(8 pm)
``` python
world.is_night() -> bool
```
Checks that it is the current time at night. From 0.8(8 pm) to 0.2(8 am)
```python
world.exists() -> bool
```
Checks the existence of a world by name.
## *gui* library
The library contains functions for accessing the properties of UI elements. Instead of gui, you should use an object wrapper that provides access to properties through the __index, __newindex meta methods:
Example:
```lua
print(document.some_button.text) -- where 'some_button' is an element id
document.some_button.text = "new text"
```
```python
gui.str(text: str, context: str) -> str
```
Returns translated text.
```python
gui.get_viewport() -> {int, int}
```
Returns size of the main container (window).
```python
gui.get_env(document: str) -> table
```
Returns environment (global variables table) of the specified document.
```python
gui.get_locales_info() -> table of tables where
key - locale id following isolangcode_ISOCOUNTRYCODE format
value - table {
name: str # name of the locale in its language
}
```
Returns information about all loaded locales (res/texts/\*).
## *inventory* library
Library for inventories interaction.
```python
inventory.get(invid: int, slot: int) -> int, int
```
Requires an inventory ID and slot index. Returns item ID and count. ID = 0 (core:empty) means that slot is empty.
```python
inventory.set(invid: int, slot: int, itemid: int, count: int)
```
Set slot content.
```python
inventory.size(invid: int) -> int
```
Returns inventory size (slots number). Throws an exception if there's no inventory having specified ID.
```python
inventory.add(invid: int, itemid: int, count: int) -> int
```
Add an item to the specified inventory. Returns remaining count if could not to add fully.
```python
inventory.get_block(x: int, y: int, z: int) -> int
```
Returns block inventory ID or 0.
```python
inventory.bind_block(invid: int, x: int, y: int, z: int)
```
Bind inventory to the specified block.
```python
inventory.unbind_block(x: int, y: int, z: int)
```
Unbind inventory from the specified block.
> [!WARNING]
> Unbound inventories will be deleted on world close.
```python
inventory.clone(invid: int) -> int
```
Create inventory copy. Returns the created copy ID.
```python
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```
Move item from slotA of invA to slotB of invB. invA may be the same as invB.
If slotB will be chosen automaticly if argument is not specified.
## *hud* library
```python
hud.open_inventory()
```
Open player inventory
```python
hud.close_inventory()
```
Close inventory
```python
hud.open_block(x: int, y: int, z: int) -> int, str
```
Open block UI and inventory. Throws an exception if block has no UI layout.
Returns block inventory ID (if *"inventory-size"=0* a virtual inventory will be created), and UI layout ID.
```python
hud.show_overlay(layoutid: str, playerinv: bool)
```
Show overlay with layout specified. Shows player inventory also if playerinv is **true**
> [!NOTE]
> Only one block may be open at same time
```python
hud.open_permanent(layoutid: str)
```
Add element to the screen. The element will be removed on world close only.
**inventory** element will be bound to the player inventory.
```python
hud.close(layoutid: str)
```
Remove an element from the screen
```python
hud.get_block_inventory() -> int
```
Get open block inventory ID or 0.
```python
hud.get_player() -> int
```
Gives the ID of the player that the UI is bound to.
```python
hud.pause()
```
Opens the pause menu
```python
hud.resume()
```
Closes the pause menu.
```python
hud.is_paused() -> bool
```
Returns true if pause menu is open.
```python
hud.is_inventory_open() -> bool
```
Returns true if inventory is open or overlay is shown.
### *time* library
```python
time.uptime() -> float
```
Returns time elapsed since the engine started.
```python
time.delta() -> float
```
Returns time elapsed since the last frame.

View File

@ -0,0 +1,41 @@
# *gui* library
The library contains functions for accessing the properties of UI elements. Instead of gui, you should use an object wrapper that provides access to properties through the __index, __newindex meta methods:
Example:
```lua
print(document.some_button.text) -- where 'some_button' is an element id
document.some_button.text = "new text"
```
```python
gui.str(text: str, context: str) -> str
```
Returns translated text.
```python
gui.get_viewport() -> {int, int}
```
Returns size of the main container (window).
```python
gui.get_env(document: str) -> table
```
Returns environment (global variables table) of the specified document.
```lua
gui.get_locales_info() -> table of tables {
name: str
}
-- where
-- key - locale id following isolangcode_ISOCOUNTRYCODE format
-- value - table {
-- name: str # locale display name
-- }
```
Returns information about all loaded locales (res/texts/\*).

View File

@ -0,0 +1,50 @@
# *hud* library
```lua
-- Open player inventory.
hud.open_inventory()
-- Close inventory.
hud.close_inventory()
-- Open block UI and inventory.
-- Throws an exception if block has no UI layout.
-- Returns block inventory ID (if *"inventory-size"=0* a virtual
-- inventory will be created), and UI layout ID.
hud.open_block(x: int, y: int, z: int) -> int, str
```
> [!NOTE]
> Only one block may be open at same time
```lua
-- Show overlay with layout specified.
-- Shows player inventory also if playerinv is true.
hud.show_overlay(layoutid: str, playerinv: bool)
-- Add element to the screen.
-- The element will be removed on world close only.
-- inventory element will be bound to the player inventory.
hud.open_permanent(layoutid: str)
-- Remove an element from the screen.
hud.close(layoutid: str)
-- Get open block inventory ID or 0.
hud.get_block_inventory() -> int
-- Gives the ID of the player that the UI is bound to.
hud.get_player() -> int
-- Opens the pause menu
hud.pause()
-- Closes the pause menu.
hud.resume()
-- Returns true if pause menu is open.
hud.is_paused() -> bool
-- Returns true if inventory is open or overlay is shown.
hud.is_inventory_open() -> bool
```

View File

@ -0,0 +1,56 @@
# *inventory* library
Library for inventories interaction.
```lua
-- ID = 0 (core:empty) means that slot is empty.
inventory.get(
-- inventory ID
invid: int,
-- slot index
slot: int
) -> int, int
-- Returns item ID and count.
-- Set slot content.
inventory.set(
-- inventory ID
invid: int,
-- slot index
slot: int,
-- item index
itemid: int,
-- item count
count: int
)
-- Returns inventory size (slots number).
-- Throws an exception if there's no inventory having specified ID.
inventory.size(invid: int) -> int
-- Add an item to the specified inventory.
-- Returns remaining count if could not to add fully.
inventory.add(invid: int, itemid: int, count: int) -> int
-- Returns block inventory ID or 0.
inventory.get_block(x: int, y: int, z: int) -> int
-- Bind inventory to the specified block.
inventory.bind_block(invid: int, x: int, y: int, z: int)
-- Unbind inventory from the specified block.
inventory.unbind_block(x: int, y: int, z: int)
```
> [!WARNING]
> Unbound inventories will be deleted on world close.
```lua
-- Create inventory copy. Returns the created copy ID.
inventory.clone(invid: int) -> int
-- Move item from slotA of invA to slotB of invB.
-- invA may be the same as invB.
-- If slotB will be chosen automaticly if argument is not specified.
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```

View File

@ -0,0 +1,13 @@
# *time* library
```python
time.uptime() -> float
```
Returns time elapsed since the engine started.
```python
time.delta() -> float
```
Returns time elapsed since the last frame.

View File

@ -0,0 +1,40 @@
# *world* library
```lua
world.get_list() -> tables array {
-- world name
name: str,
-- world icon/preview (loading automatically)
icon: str
}
-- Returns current day time in range \[0.0-1.0\]
-- where 0.0 and 1.0 - midnight, 0.5 - noon.
world.get_day_time() -> number
-- Set day time value.
world.set_day_time(time: number)
-- Sets the specified day time cycle speed.
world.set_day_time_speed(value: number)
-- Returns the day time cycle speed.
world.get_day_time_speed() -> number
-- Returns total time passed in the world.
world.get_total_time() -> number
-- Returns world seed.
world.get_seed() -> int
-- Proves that this is the current time during the day
-- from 0.333(8 am) to 0.833(8 pm).
world.is_day() -> boolean
-- Checks that it is the current time at night
-- from 0.833(8 pm) to 0.333(8 am).
world.is_night() -> bool
-- Checks the existence of a world by name.
world.exists() -> bool
```

464
doc/en/world-generator.md Normal file
View File

@ -0,0 +1,464 @@
# World Generator
## Table of Contents
- [Basic concepts](#basic-concepts)
- [Global variables](#global-variables)
- [Configuration file](#configuration-file)
- [Fragments](#fragments)
- [Structures](#structures)
- [Biomes](#biomes)
* [Biome parameters](#biome-parameters)
* [Biome selection](#biome-selection)
- [Heightmap](#heightmap)
* [Constructor](#constructor)
* [Unary operations](#unary-operations)
* [binary operations](#binary-operations)
* [heightmap:dump(...)](#heightmapdump)
* [heightmap:noise(...)](#heightmapnoise)
* [heightmap:cellnoise(...)](#heightmapcellnoise)
* [heightmap:resize(...)](#heightmapresize)
* [heightmap:crop(...)](#heightmapcrop)
* [heightmap:at(x, y)](#heightmapatx-y)
- [VoxelFragment (fragment in Lua)](#voxelfragment-fragment-in-lua)
- [Generating a height map](#generating-a-height-map)
- [Manual structures placement](#manual-structures-placement)
* [Structure/tunnel placements](#structuretunnel-placements)
* [Small structures placement](#small-structures-placement)
* [Wide structures placement](#wide-structures-placement)
- [Structural air](#structural-air)
## Basic concepts
Concepts used in the text below.
- **Combined array/object** - TOML or JSON file that is combined from several versions in different packs, which allows adding data to it from outside. The fields of the combined object are overwritten in order from first to last, just like other resources in the packs. In the case of a combined array, a check for duplicates is **not** performed.
- **Biome** - information that determines what blocks and what layers the terrain is generated from, as well as a set of plants, structures.
- **Plant** - a block randomly placed on the surface.
- **Small structure** - a structure whose size does not exceed the size of a chunk. Example: trees.
## Configuration file
The world generator is recognized if the file `generators/generator_name.toml` is present. Other files related to the generator are located in the directory `generators/generator_name.files/`:
- biomes.toml - biome definitions
- structures.toml - structure definitions
- script.lua - generator script
- fragments - directory where fragment files are located
The main properties described in the configuration file:
- **caption** - the generator display name. By default, it is generated from the id.
- **biome-parameters** - the number of biome selection parameters (from 0 to 4). Default: 0.
- **sea-level** - sea level (below this level, sea-layers will be generated instead of air). Default: 0.
- **biomes-bpd** - number of blocks per point of the biome selection parameter map. Default: 8.
- **heights-bpd** - number of blocks per point of the height map. Default: 8.
- **wide-structs-chunks-radius** - maximum radius for placing 'wide' structures, measured in chunks.
## Global variables
The following variables are available in the generator script:
- `SEED` - world generation seed
- `__DIR__` - generator directory (`pack:generators/generator_name.files/`)
- `__FILE__` - script file (`pack:generators/generator_name.files/script.lua`)
## Fragments
A fragment is a region of the world, like a chunk, saved for later use, limited by a certain width, height and length. A fragment can contain data not only blocks, but also the block inventories and entities. Unlike a chunk, the size of a fragment is arbitrary.
Fragment can be created using the `fragment.save` command, or the `generation.create_fragment` function.
Fragments used by the generator must present in the directory:
`generators/generator_name.files/fragments/`
## Structures
A structure is a set of rules for inserting a fragment into the world by the generator. It currently has no properties, being created as empty objects in the `generators/generator_name.files/structures.json` file. Example:
```lua
{
"tree0": {},
"tree1": {},
"tree2": {},
"tower": {},
"coal_ore0": {}
}
```
Currently, the name of the structure must match the name of the fragment used.
## Biomes
A biome defines what blocks and layers the terrain is generated from, as well as a set of plants and structures.
Biomes are defined in a combined object:
`generators/generator_name.files/biomes.toml`
Let's look at the biome structure using the forest example from the base:demo generator:
```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}
]
```
- key 'forest' - biome name
- parameters - weights and central values of the parameters for the biome. See the [biome selection](#biome-selection) section. The number of entries must match the number of biome selection parameters.
- layers - block layers from top to bottom.
- height - layer height in blocks. -1 is used to mark the filler layer, of which there can only be one. Its height is calculated automatically.
- block - full block name
- below-sea-level - whether the layer can be generated below sea level (example: turf). If false, when generated below sea level, the layer will be replaced by the next one.
- sea-layers - ocean layers. The position of the top layer coincides with the sea level height.
- plant-chance - probability of generating a plant on a surface block.
- plants - plants randomly placed on the surface.
- weight - weight directly affecting the chance of selecting a specific plant.
- block - plant block
- structure-chance - probability of generating a small structure on a surface block.
- structures - structures randomly placed on the surface.
- name - name of the structure declared in `structures.json`.
- weight - weight directly affecting the chance of choosing a specific structure.
### Biome Parameters
The generator parameter `biome-parameters` defines the number of biome selection parameters (examples: temperature, humidity).
Biome parameter value maps are generated in the same way as height maps.
It is required to implement the function:
```lua
-- x, y - map start position (in dots)
-- w, h - map width and height (in dots)
-- bpd - (blocks per dot) number of blocks per dot (scale)
function generate_biome_parameters(x, y, w, h, bpd)
-- creating heightmaps for each biome parameter
-- ...
return comma-separated_maps
end
-- example
function generate_biome_parameters(x, y, w, h, s)
-- temperature map
local tempmap = Heightmap(w, h)
tempmap.noiseSeed = SEED + 5324
tempmap:noise({x, y}, 0.04*s, 6)
tempmap:pow(3)
-- humidity map
local hummap = Heightmap(w, h)
hummap.noiseSeed = SEED + 953
hummap:noise({x, y}, 0.04*s, 6)
hummap:pow(3)
return tempmap, hummap
end
```
### Biome selection
After generating the parameter maps for each biome, scores are calculated for all parameters:
$score = \frac{|V - V_b|}{W_b}$
Where $V$ is the parameter value, $V_b$ is the central parameter value for the biome, $W_b$ is the biome weight for the parameter.
The generator selects the biome with the **least** sum of parameter scores.
>[!WARNING]
> If the parameter values and weights are not set up correctly, biomes may have an effect similar to the depth conflict in 3D graphics when two surfaces overlap.
>
In the case of biomes, the pattern looks random due to the distortion of these 'surfaces' by the noise used to generate the parameter maps.
>
To get rid of the effect, you can either adjust the weights or parameter values of the biomes, or increase the difference in the generation of the parameter maps.
## Heightmap
Heightmap is a class for working with heightmaps (arbitrarily sized matrices of floating point numbers).
### Constructor
The heightmap constructor requires integer width and height.
```lua
local map = Heightmap(width, height)
```
### Unary Operations
Operations apply to all height values.
```lua
map:abs()
```
Casts height values to absolute.
### Binary Operations
Operations using a second map or a scalar.
Arithmetic operations:
```lua
-- Addition
map:add(value: Heightmap|number)
-- Subtraction
map:sub(value: Heightmap|number)
-- Multiplication
map:mul(value: Heightmap|number)
-- Exponentiation
map:pow(value: Heightmap|number)
```
Other operations:
```lua
-- Minimum
map:min(value: Heightmap|number)
-- Maximum
map:max(value: Heightmap|number)
-- Mixing
map:mixin(value: Heightmap|number, t: Heightmap|number)
-- t - mixing factor from 0.0 to 1.0
-- mixing is performed according to the formula:
-- map_value * (1.0 - t) + value * t
```
### heightmap:dump(...)
A method used for debugging, creates an image based on a heightmap by converting values from the range `[-1.0, 1.0]` to brightness values `[0, 255]`, saving it to the specified file.
```lua
map:dump('export:test.png')
```
### heightmap:noise(...)
A method that generates simplex noise, adding it to the existing values.
The noise seed can be specified in the `map.noiseSeed` field.
```lua
map:noise(
-- coordinate offset
offset: {number, number},
-- coordinate scaling factor
scale: number,
-- number of noise octaves (default: 1)
[optional] octaves: integer,
-- noise amplitude multiplier (default: 1.0)
[optional] multiplier: number,
-- X coordinate offset map for noise generation
[optional] shiftMapX: Heightmap,
-- Y coordinate offset map for noise generation
[optional] shiftMapY: Heightmap,
) -> nil
```
Noise visualization with octaves 1, 2, 3, 4, and 5.
![image](../images/simplex-noise.gif)
### heightmap:cellnoise(...)
Analog of heightmap:noise that generates cellular noise.
The noise seed can be specified in the `map.noiseSeed` field.
![image](../images/cell-noise.gif)
### heightmap:resize(...)
```lua
map:resize(width, height, interpolation)
```
Changes the heightmap size.
Available interpolation modes:
- 'nearest' - no interpolation
- 'linear' - bilinear interpolation
### heightmap:crop(...)
```lua
map:crop(x, y, width, height)
```
Crops the heightmap to the specified area.
### heightmap:at(x, y)
```lua
map:at(x, y) --> number
```
Returns the height value at the specified position.
## VoxelFragment (fragment in Lua)
A fragment is created by calling the function:
```lua
generation.create_fragment(
-- point A
a: vec3,
-- point B
b: vec3,
-- automatically crop the fragment if possible
crop: bool
) -> VoxelFragment
```
A fragment can be loaded from a file:
```lua
generation.load_fragment(
-- fragment file
filename: str
) -> VoxelFragment
```
A fragment can be saved to a file:
```lua
generation.save_fragment(
-- fragment to save
fragment: VoxelFragment,
-- file
filename: str
) -> nil
```
The fragment size is available as the `size` property.
A fragment can be cropped to fit its contents (air is ignored) by calling the `fragment:crop()` method.
## Generating a height map
By default, the engine generates a height map consisting of zeros.
To generate custom heightmaps, you need to implement the function:
```lua
function generate_heightmap(
x, y, -- offset of the heightmap
w, h, -- size of the heightmap expected by the engine
bpd, -- number of blocks per dot - scale
[optional] inputs -- array of input maps of biome parameters
-- (see the heightmap-inputs property of the generator)
) --> Heightmap
```
An example of generating a heightmap from simplex noise with reduction
to the desired range:
```lua
function generate_heightmap(x, y, w, h, bpd)
-- create a heightmap with a given size
local map = Heightmap(w, h)
-- set the noise seed
map.noiseSeed = SEED
-- noise with a scale of 1/10 by 4 octaves with an amplitude of 0.5
map:noise({x, y}, 0.1*bpd, 4, 0.5)
-- shift heights to positive range
map:add(0.5)
return map
end
```
## Manual structures placement
### Structure/tunnel placements
Placement of a structure/line is an array of a given
set of parameters.
Structure:
```lua
{structure_name, structure_position, rotation, [optional] priority}
```
Where:
- structure_name - a string containing the name of the structure, registered in structures.toml.
- structure_position - a vec3 (an array of three numbers) relative to the position of the chunk.
- rotation - a number from 0 to 3 indicating the rotation of the structure along the Y axis.
- priority - a number determining the order in which structures are placed. Structures with lower priority are overlapped by structures with higher priority.
Tunnel:
```lua
{":line", filler_block, point_a, point_b, radius}
```
Where:
- filler_block - the numeric id of the block that the structure will consist of.
- point_a, point_b - vec3, vec3 positions of the start and end of the tunnel.
- radius - radius of the tunnel in blocks
### Small structures placement
```lua
function place_structures(
x, z, -- start position of the region in blocks
w, d, -- size of the region in blocks
heights, -- height map of the chunk
chunk_height, -- height of the chunk
) --> array of structure placements
```
Structures can be placed outside the chunk, but not more than one chunk away.
Example:
```lua
function place_structures(x, z, w, d, hmap, chunk_height)
local placements = {}
local height = hmap:at(w/2, h/2) * chunk_height
-- places the tower in the center of the chunk
table.insert(placements, {
'tower', {w/2, height, d/2}, math.random() * 4, 2
})
return placements
end
```
### Wide structures placement
Structures and tunnels can be placed outside a chunk, but no further than the number of chunks specified in the `wide-structs-chunks-radius` property of the generator.
Unlike the previous function, no heightmap is passed here, since the call occurs early in the chunk generation.
```lua
function place_structures_wide(
x, z, -- start position of the region in blocks
w, d, -- size of the region in blocks
chunk_height, -- height of the chunk
) --> array of structure / tunnel placements
```
## Structural Air
`core:struct_air` - a block that should be used in chunks to mark empty space that should not be filled with blocks when generated in the world.
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">

BIN
doc/images/cell-noise.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -1,16 +1,21 @@
# Документация
Документация версии движка 0.23, находящейся в разработке.
[Документация стабильной версии (0.22.x)](https://github.com/MihailRis/VoxelEngine-Cpp/blob/release-0.22/doc/ru/main-page.md)
## Разделы
- [Рекомендации по использованию движка](engine-use-recommendations.md)
- [XML разметка интерфейса](xml-ui-layouts.md)
- [Аудио](audio.md)
- [Движок генерации мира](world-generator.md)
- [Консоль](console.md)
- [Контент‐паки](content-packs.md)
- [Модели блоков](block-models.md)
- [Предзагрузка ассетов](assets-preload.md)
- [Рекомендации по использованию движка](engine-use-recommendations.md)
- [Ресурсы (resources.json)](resources.md)
- [Риггинг](rigging.md)
- [Свойства блоков](block-properties.md)
- [Свойства предметов](item-properties.md)
- [XML разметка интерфейса](xml-ui-layouts.md)
- [Предзагрузка ассетов](assets-preload.md)
- [Аудио](audio.md)
- [Скриптинг](scripting.md)
- [Консоль](console.md)
- [Модели блоков](block-models.md)
- [Риггинг](rigging.md)
- [Ресурсы (resources.json)](resources.md)

View File

@ -10,14 +10,19 @@
- [Сущности и компоненты](scripting/ecs.md)
- [Библиотеки](#)
- [block](scripting/builtins/libblock.md)
- [item](scripting/builtins/libitem.md)
- [entities](scripting/builtins/libentities.md)
- [cameras](scripting/builtins/libcameras.md)
- [entities](scripting/builtins/libentities.md)
- [gui](scripting/builtins/libgui.md)
- [hud](scripting/builtins/libhud.md)
- [inventory](scripting/builtins/libinventory.md)
- [item](scripting/builtins/libitem.md)
- [mat4](scripting/builtins/libmat4.md)
- [pack](scripting/builtins/libpack.md)
- [player](scripting/builtins/libplayer.md)
- [quat](scripting/builtins/libquat.md)
- [time](scripting/builtins/libtime.md)
- [vec2, vec3, vec4](scripting/builtins/libvecn.md)
- [world](scripting/builtins/libworld.md)
- [Модуль core:bit_converter](scripting/modules/core_bit_converter.md)
- [Модуль core:data_buffer](scripting/modules/core_data_buffer.md)
- [Модули core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md)
@ -37,263 +42,3 @@
```lua
require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается)
```
## Библиотека *world*
```python
world.get_list() -> массив таблиц {
name: str,
icon: str
}
```
Возвращает информацию о мирах: название и предпросмотр (автоматически загружаемая текстура).
```python
world.get_day_time() -> number
```
Возвращает текущее игровое время от 0.0 до 1.0, где 0.0 и 1.0 - полночь, 0.5 - полдень.
```python
world.set_day_time(time: number)
```
Устанавливает указанное игровое время.
```python
world.set_day_time_speed(value: number)
```
Устанавливает указанную скорость для игрового времени.
```python
world.get_day_time_speed() -> number
```
Возвращает скорость для игрового времени.
```python
world.get_total_time() -> number
```
Возвращает общее суммарное время, прошедшее в мире
```python
world.get_seed() -> int
```
Возвращает зерно мира.
```python
world.exists() -> bool
```
Проверяет существование мира по имени.
```python
world.is_day() -> bool
```
Проверяет является ли текущее время днём. От 0.2(8 утра) до 0.8(8 вечера)
```python
world.is_night() -> bool
```
Проверяет является ли текущее время ночью. От 0.8(8 вечера) до 0.2(8 утра)
## Библиотека *gui*
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
```lua
print(document.some_button.text) -- где 'some_button' - id элемета
document.some_button.text = "новый текст"
```
В скрипте макета `layouts/файл_макета.xml` - `layouts/файл_макета.xml.lua` уже доступна переменная **document** содержащая объект класса Document
```python
gui.str(text: str, context: str) -> str
```
Возращает переведенный текст.
```python
gui.get_viewport() -> {int, int}
```
Возвращает размер главного контейнера (окна).
```python
gui.get_env(document: str) -> table
```
Возвращает окружение (таблица глобальных переменных) указанного документа.
```python
get_locales_info() -> таблица таблиц где
ключ - id локали в формате isolangcode_ISOCOUNTRYCODE
значение - таблица {
name: str # название локали на её языке
}
```
Возвращает информацию о всех загруженных локалях (res/texts/\*).
## Библиотека *inventory*
Библиотека функций для работы с инвентарем.
```python
inventory.get(invid: int, slot: int) -> int, int
```
Принимает id инвентаря и индекс слота. Возвращает id предмета и его количество. id = 0 (core:empty) обозначает, что слот пуст.
```python
inventory.set(invid: int, slot: int, itemid: int, count: int)
```
Устанавливает содержимое слота.
```python
inventory.size(invid: int) -> int
```
Возращает размер инвентаря (число слотов). Если указанного инвентаря не существует, бросает исключение.
```python
inventory.add(invid: int, itemid: int, count: int) -> int
```
Добавляет предмет в инвентарь. Если не удалось вместить все количество, возвращает остаток.
```python
inventory.get_block(x: int, y: int, z: int) -> int
```
Функция возвращает id инвентаря указанного блока. Если блок не может иметь инвентарь - возвращает 0.
```python
inventory.bind_block(invid: int, x: int, y: int, z: int)
```
Привязывает указанный инвентарь к блоку.
```python
inventory.unbind_block(x: int, y: int, z: int)
```
Отвязывает инвентарь от блока.
> [!WARNING]
> Инвентари, не привязанные ни к одному из блоков, удаляются при выходе из мира.
```python
inventory.clone(invid: int) -> int
```
Создает копию инвентаря и возвращает id копии. Если копируемого инвентаря не существует, возвращает 0.
```python
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```
Перемещает предмет из slotA инвентаря invA в slotB инвентаря invB.
invA и invB могут указывать на один инвентарь.
slotB будет выбран автоматически, если не указывать явно.
## Библиотека hud
```python
hud.open_inventory()
```
Открывает инвентарь
```python
hud.close_inventory()
```
Закрывает инвентарь
```python
hud.open_block(x: int, y: int, z: int) -> int, str
```
Открывает инвентарь и UI блока. Если блок не имеет макета UI - бросается исключение.
Возвращает id инвентаря блока (при *"inventory-size"=0* создаётся виртуальный инвентарь, который удаляется после закрытия), и id макета UI.
```python
hud.show_overlay(layoutid: str, playerinv: bool)
```
Показывает элемент в режиме оверлея. Также показывает инвентарь игрока, если playerinv - **true**
> [!NOTE]
> Одновременно может быть открыт только один блок
```python
hud.open_permanent(layoutid: str)
```
Добавляет постоянный элемент на экран. Элемент не удаляется при закрытии инвентаря. Чтобы не перекрывать затенение в режиме инвентаря нужно установить z-index элемента меньшим чем -1. В случае тега inventory, произойдет привязка слотов к инвентарю игрока.
```python
hud.close(layoutid: str)
```
Удаляет элемент с экрана.
```python
hud.get_block_inventory() -> int
```
Дает ID инвентаря открытого блока или 0.
```python
hud.get_player() -> int
```
Дает ID игрока, к которому привязан пользовательский интерфейс.
```python
hud.pause()
```
Открывает меню паузы.
```python
hud.resume()
```
Закрывает меню паузы.
```python
hud.is_paused() -> bool
```
Возвращает true если открыто меню паузы.
```python
hud.is_inventory_open() -> bool
```
Возвращает true если открыт инвентарь или оверлей.
## Библиотека time
```python
time.uptime() -> float
```
Возвращает время с момента запуска движка в секундах.
```python
time.delta() -> float
```
Возвращает дельту времени (время прошедшее с предыдущего кадра)

View File

@ -0,0 +1,38 @@
# Библиотека *gui*
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
```lua
print(document.some_button.text) -- где 'some_button' - id элемета
document.some_button.text = "новый текст"
```
В скрипте макета `layouts/файл_макета.xml` - `layouts/файл_макета.xml.lua` уже доступна переменная **document** содержащая объект класса Document
```python
gui.str(text: str, context: str) -> str
```
Возращает переведенный текст.
```python
gui.get_viewport() -> {int, int}
```
Возвращает размер главного контейнера (окна).
```python
gui.get_env(document: str) -> table
```
Возвращает окружение (таблица глобальных переменных) указанного документа.
```python
gui.get_locales_info() -> таблица таблиц где
ключ - id локали в формате isolangcode_ISOCOUNTRYCODE
значение - таблица {
name: str # название локали на её языке
}
```
Возвращает информацию о всех загруженных локалях (res/texts/\*).

View File

@ -0,0 +1,52 @@
# Библиотека hud
```lua
-- Открывает инвентарь.
hud.open_inventory()
-- Закрывает инвентарь.
hud.close_inventory()
-- Открывает инвентарь и UI блока.
-- Если блок не имеет макета UI - бросается исключение.
-- Возвращает id инвентаря блока
-- (при *"inventory-size"=0* создаётся виртуальный инвентарь,
-- который удаляется после закрытия), и id макета UI.
hud.open_block(x: int, y: int, z: int) -> int, str
```
> [!NOTE]
> Одновременно может быть открыт только один блок
```lua
-- Показывает элемент в режиме оверлея.
-- Также показывает инвентарь игрока, если playerinv - **true**.
hud.show_overlay(layoutid: str, playerinv: bool)
-- Добавляет постоянный элемент на экран. Элемент не удаляется при
-- закрытии инвентаря. Чтобы не перекрывать затенение в режиме
-- инвентаря нужно установить z-index элемента меньшим чем -1.
-- В случае тега inventory, произойдет привязка слотов к инвентарю игрока.
hud.open_permanent(layoutid: str)
-- Удаляет элемент с экрана.
hud.close(layoutid: str)
-- Дает ID инвентаря открытого блока или 0.
hud.get_block_inventory() -> int
-- Дает ID игрока, к которому привязан пользовательский интерфейс.
hud.get_player() -> int
-- Открывает меню паузы.
hud.pause()
-- Закрывает меню паузы.
hud.resume()
-- Возвращает true если открыто меню паузы.
hud.is_paused() -> bool
-- Возвращает true если открыт инвентарь или оверлей.
hud.is_inventory_open() -> bool
```

View File

@ -0,0 +1,66 @@
# Библиотека *inventory*
Библиотека функций для работы с инвентарем.
```lua
-- Возвращает id предмета и его количество. id = 0 (core:empty) обозначает, что слот пуст.
inventory.get(
-- id инвентаря
invid: int,
-- индекс слота
slot: int
) -> int, int
-- Устанавливает содержимое слота.
inventory.set(
-- id инвентаря
invid: int,
-- индекс слота
slot: int,
-- id предмета
itemid: int,
-- количество предмета
count: int
)
-- Возращает размер инвентаря (число слотов).
-- Если указанного инвентаря не существует, бросает исключение.
inventory.size(invid: int) -> int
-- Добавляет предмет в инвентарь.
-- Если не удалось вместить все количество, возвращает остаток.
inventory.add(
-- id инвентаря
invid: int,
-- id предмета
itemid: int,
-- количество предмета
count: int
) -> int
-- Функция возвращает id инвентаря блока.
-- Если блок не может иметь инвентарь - возвращает 0.
inventory.get_block(x: int, y: int, z: int) -> int
-- Привязывает указанный инвентарь к блоку.
inventory.bind_block(invid: int, x: int, y: int, z: int)
-- Отвязывает инвентарь от блока.
inventory.unbind_block(x: int, y: int, z: int)
```
> [!WARNING]
> Инвентари, не привязанные ни к одному из блоков, удаляются при выходе из мира.
```lua
-- Создает копию инвентаря и возвращает id копии.
-- Если копируемого инвентаря не существует, возвращает 0.
inventory.clone(invid: int) -> int
-- Перемещает предмет из slotA инвентаря invA в slotB инвентаря invB.
-- invA и invB могут указывать на один инвентарь.
-- slotB будет выбран автоматически, если не указывать явно.
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```

View File

@ -0,0 +1,13 @@
# Библиотека time
```python
time.uptime() -> float
```
Возвращает время с момента запуска движка в секундах.
```python
time.delta() -> float
```
Возвращает дельту времени (время прошедшее с предыдущего кадра)

View File

@ -0,0 +1,38 @@
# Библиотека *world*
```lua
-- Возвращает информацию о мирах.
world.get_list() -> массив таблиц {
-- название мира
name: str,
-- предпросмотр (автоматически загружаемая текстура)
icon: str
}
-- Возвращает текущее игровое время от 0.0 до 1.0, где 0.0 и 1.0 - полночь, 0.5 - полдень.
world.get_day_time() -> number
-- Устанавливает указанное игровое время.
world.set_day_time(time: number)
-- Устанавливает указанную скорость смены времени суток.
world.set_day_time_speed(value: number)
-- Возвращает скорость скорость смены времени суток.
world.get_day_time_speed() -> number
-- Возвращает суммарное время, прошедшее в мире.
world.get_total_time() -> number
-- Возвращает зерно мира.
world.get_seed() -> int
-- Проверяет существование мира по имени.
world.exists() -> bool
-- Проверяет является ли текущее время днём. От 0.333(8 утра) до 0.833(8 вечера).
world.is_day() -> bool
-- Проверяет является ли текущее время ночью. От 0.833(8 вечера) до 0.333(8 утра).
world.is_night() -> bool
```

469
doc/ru/world-generator.md Normal file
View File

@ -0,0 +1,469 @@
# Генератор мира
## Содержание
- [Основные понятия](#основные-понятия)
- [Глобальные переменные](#глобальные-переменные)
- [Файл конфигурации](#файл-конфигурации)
- [Фрагменты](#фрагменты)
- [Структуры](#структуры)
- [Биомы](#биомы)
* [Параметры биомов](#параметры-биомов)
* [Выбор биома](#выбор-биома)
- [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** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 8.
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 8.
- **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.
![image](../images/simplex-noise.gif)
### heightmap:cellnoise(...)
Аналог heightmap:noise генерирующий клеточный шум.
Зерно шума может быть указано в поле `map.noiseSeed`.
![image](../images/cell-noise.gif)
### 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">

View File

@ -2,13 +2,13 @@ local _, dir = parse_path(__DIR__)
local ores = require "base:generation/ores"
ores.load(dir)
function place_structures(x, z, w, d, seed, hmap, chunk_height)
function place_structures(x, z, w, d, hmap, chunk_height)
local placements = {}
ores.place(placements, x, z, w, d, seed, hmap, chunk_height)
ores.place(placements, x, z, w, d, SEED, hmap, chunk_height)
return placements
end
function place_structures_wide(x, z, w, d, seed, chunk_height)
function place_structures_wide(x, z, w, d, chunk_height)
local placements = {}
if math.random() < 0.05 then -- generate caves
local sx = x + math.random() * 10 - 5
@ -43,22 +43,22 @@ function place_structures_wide(x, z, w, d, seed, chunk_height)
return placements
end
function generate_heightmap(x, y, w, h, seed, s, inputs)
function generate_heightmap(x, y, w, h, s, inputs)
local umap = Heightmap(w, h)
local vmap = Heightmap(w, h)
umap.noiseSeed = seed
vmap.noiseSeed = seed
umap.noiseSeed = SEED
vmap.noiseSeed = SEED
vmap:noise({x+521, y+70}, 0.1*s, 3, 25.8)
vmap:noise({x+95, y+246}, 0.15*s, 3, 25.8)
local map = Heightmap(w, h)
map.noiseSeed = seed
map.noiseSeed = SEED
map:noise({x, y}, 0.8*s, 4, 0.02)
map:cellnoise({x, y}, 0.1*s, 3, 0.3, umap, vmap)
map:add(0.7)
local rivermap = Heightmap(w, h)
rivermap.noiseSeed = seed
rivermap.noiseSeed = SEED
rivermap:noise({x+21, y+12}, 0.1*s, 4)
rivermap:abs()
rivermap:mul(2.0)
@ -67,21 +67,21 @@ function generate_heightmap(x, y, w, h, seed, s, inputs)
map:mul(rivermap)
local desertmap = Heightmap(w, h)
desertmap.noiseSeed = seed
desertmap.noiseSeed = SEED
desertmap:cellnoise({x+52, y+326}, 0.3*s, 2, 0.2)
desertmap:add(0.4)
map:mixin(desertmap, inputs[1])
return map
end
function generate_biome_parameters(x, y, w, h, seed, s)
function generate_biome_parameters(x, y, w, h, s)
local tempmap = Heightmap(w, h)
tempmap.noiseSeed = seed + 5324
tempmap.noiseSeed = SEED + 5324
tempmap:noise({x, y}, 0.04*s, 6)
tempmap:mul(0.5)
tempmap:add(0.5)
local hummap = Heightmap(w, h)
hummap.noiseSeed = seed + 953
hummap.noiseSeed = SEED + 953
hummap:noise({x, y}, 0.04*s, 6)
tempmap:pow(3)
hummap:pow(3)

View File

@ -77,12 +77,12 @@ static int l_world_exists(lua::State* L) {
static int l_world_is_day(lua::State* L) {
auto daytime = level->getWorld()->getInfo().daytime;
return lua::pushboolean(L, daytime >= 0.2 && daytime <= 0.8);
return lua::pushboolean(L, daytime >= 0.333 && daytime <= 0.833);
}
static int l_world_is_night(lua::State* L) {
auto daytime = level->getWorld()->getInfo().daytime;
return lua::pushboolean(L, daytime < 0.2 || daytime > 0.8);
return lua::pushboolean(L, daytime < 0.333 || daytime > 0.833);
}
const luaL_Reg worldlib[] = {

View File

@ -256,6 +256,7 @@ static std::unordered_map<std::string, lua_CFunction> methods {
{"cellnoise", lua::wrap<l_noise<FNL_NOISE_CELLULAR>>},
{"pow", lua::wrap<l_binop_func<util::pow>>},
{"add", lua::wrap<l_binop_func<std::plus>>},
{"sub", lua::wrap<l_binop_func<std::minus>>},
{"mul", lua::wrap<l_binop_func<std::multiplies>>},
{"min", lua::wrap<l_binop_func<util::min>>},
{"max", lua::wrap<l_binop_func<util::max>>},

View File

@ -24,10 +24,13 @@ static debug::Logger logger("generator-scripting");
class LuaGeneratorScript : public GeneratorScript {
State* L;
const GeneratorDef& def;
scriptenv env;
scriptenv env = nullptr;
fs::path file;
std::string dirPath;
public:
LuaGeneratorScript(State* L, const GeneratorDef& def, scriptenv env)
: L(L), def(def), env(std::move(env)) {
LuaGeneratorScript(State* L, const GeneratorDef& def, const fs::path& file, const std::string& dirPath)
: L(L), def(def), file(file), dirPath(dirPath) {
}
virtual ~LuaGeneratorScript() {
@ -37,10 +40,33 @@ public:
}
}
void initialize(uint64_t seed) override {
env = create_environment(L);
stackguard _(L);
pushenv(L, *env);
pushstring(L, dirPath);
setfield(L, "__DIR__");
pushstring(L, dirPath + "/script.lua");
setfield(L, "__FILE__");
pushinteger(L, seed);
setfield(L, "SEED");
pop(L);
if (fs::exists(file)) {
std::string src = files::read_string(file);
logger.info() << "script (generator) " << file.u8string();
pop(L, execute(L, *env, src, file.u8string()));
} else {
// Use default (empty) script
pop(L, execute(L, *env, "", "<empty>"));
}
}
std::shared_ptr<Heightmap> generateHeightmap(
const glm::ivec2& offset,
const glm::ivec2& size,
uint64_t seed,
uint bpd,
const std::vector<std::shared_ptr<Heightmap>>& inputs
) override {
@ -48,7 +74,6 @@ public:
if (getfield(L, "generate_heightmap")) {
pushivec_stack(L, offset);
pushivec_stack(L, size);
pushinteger(L, seed);
pushinteger(L, bpd);
if (!inputs.empty()) {
size_t inputsNum = def.heightmapInputs.size();
@ -58,7 +83,7 @@ public:
rawseti(L, i+1);
}
}
if (call_nothrow(L, 6 + (!inputs.empty()))) {
if (call_nothrow(L, 5 + (!inputs.empty()))) {
auto map = touserdata<LuaHeightmap>(L, -1)->getHeightmap();
pop(L, 2);
return map;
@ -69,7 +94,7 @@ public:
}
std::vector<std::shared_ptr<Heightmap>> generateParameterMaps(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd
const glm::ivec2& offset, const glm::ivec2& size, uint bpd
) override {
std::vector<std::shared_ptr<Heightmap>> maps;
@ -78,9 +103,8 @@ public:
if (getfield(L, "generate_biome_parameters")) {
pushivec_stack(L, offset);
pushivec_stack(L, size);
pushinteger(L, seed);
pushinteger(L, bpd);
if (call_nothrow(L, 6, biomeParameters)) {
if (call_nothrow(L, 5, biomeParameters)) {
for (int i = biomeParameters-1; i >= 0; i--) {
maps.push_back(
touserdata<LuaHeightmap>(L, -1-i)->getHeightmap());
@ -166,7 +190,6 @@ public:
std::vector<Placement> placeStructuresWide(
const glm::ivec2& offset,
const glm::ivec2& size,
uint64_t seed,
uint chunkHeight
) override {
std::vector<Placement> placements {};
@ -176,9 +199,8 @@ public:
if (getfield(L, "place_structures_wide")) {
pushivec_stack(L, offset);
pushivec_stack(L, size);
pushinteger(L, seed);
pushinteger(L, chunkHeight);
if (call_nothrow(L, 6, 1)) {
if (call_nothrow(L, 5, 1)) {
int len = objlen(L, -1);
for (int i = 1; i <= len; i++) {
rawgeti(L, i);
@ -194,8 +216,10 @@ public:
}
std::vector<Placement> placeStructures(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight
const glm::ivec2& offset,
const glm::ivec2& size,
const std::shared_ptr<Heightmap>& heightmap,
uint chunkHeight
) override {
std::vector<Placement> placements {};
@ -204,10 +228,9 @@ public:
if (getfield(L, "place_structures")) {
pushivec_stack(L, offset);
pushivec_stack(L, size);
pushinteger(L, seed);
newuserdata<LuaHeightmap>(L, heightmap);
pushinteger(L, chunkHeight);
if (call_nothrow(L, 7, 1)) {
if (call_nothrow(L, 6, 1)) {
int len = objlen(L, -1);
for (int i = 1; i <= len; i++) {
rawgeti(L, i);
@ -224,28 +247,11 @@ public:
};
std::unique_ptr<GeneratorScript> scripting::load_generator(
const GeneratorDef& def, const fs::path& file, const std::string& dirPath
const GeneratorDef& def,
const fs::path& file,
const std::string& dirPath
) {
auto L = create_state(*engine->getPaths(), StateType::GENERATOR);
auto env = create_environment(L);
stackguard _(L);
pushenv(L, *env);
pushstring(L, dirPath);
setfield(L, "__DIR__");
pushstring(L, dirPath + "/script.lua");
setfield(L, "__FILE__");
pop(L);
if (fs::exists(file)) {
std::string src = files::read_string(file);
logger.info() << "script (generator) " << file.u8string();
pop(L, execute(L, *env, src, file.u8string()));
} else {
// Use default (empty) script
pop(L, execute(L, *env, "", "<empty>"));
}
return std::make_unique<LuaGeneratorScript>(L, def, std::move(env));
return std::make_unique<LuaGeneratorScript>(L, def, file, dirPath);
}

View File

@ -123,17 +123,17 @@ class GeneratorScript {
public:
virtual ~GeneratorScript() = default;
virtual void initialize(uint64_t seed) = 0;
/// @brief Generate a heightmap with values in range 0..1
/// @param offset position of the heightmap in the world
/// @param size size of the heightmap
/// @param seed world seed
/// @param bpd blocks per dot
/// @param inputs biome parameter maps passed to generate_heightmap
/// @return generated heightmap (can't be nullptr)
virtual std::shared_ptr<Heightmap> generateHeightmap(
const glm::ivec2& offset,
const glm::ivec2& size,
uint64_t seed,
uint bpd,
const std::vector<std::shared_ptr<Heightmap>>& inputs
) = 0;
@ -141,13 +141,11 @@ public:
/// @brief Generate a biomes parameters maps
/// @param offset position of maps in the world
/// @param size maps size
/// @param seed world seed
/// @param bpd blocks per dot
/// @return generated maps (can't be nullptr)
virtual std::vector<std::shared_ptr<Heightmap>> generateParameterMaps(
const glm::ivec2& offset,
const glm::ivec2& size,
uint64_t seed,
uint bpd
) = 0;
@ -156,12 +154,10 @@ public:
/// wide-structs-chunks-radius
/// @param offset position of the area
/// @param size size of the area (blocks)
/// @param seed world seed
/// @param chunkHeight chunk height to use as heights multiplier
virtual std::vector<Placement> placeStructuresWide(
const glm::ivec2& offset,
const glm::ivec2& size,
uint64_t seed,
const glm::ivec2& offset,
const glm::ivec2& size,
uint chunkHeight
) = 0;
@ -169,13 +165,15 @@ public:
/// placed to nearest chunks also (position of out area).
/// @param offset position of the area
/// @param size size of the area (blocks)
/// @param seed world seed
/// @param heightmap area heightmap
/// @param chunkHeight chunk height to use as heights multiplier
/// @return structure & line placements
virtual std::vector<Placement> placeStructures(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight) = 0;
const glm::ivec2& offset,
const glm::ivec2& size,
const std::shared_ptr<Heightmap>& heightmap,
uint chunkHeight
) = 0;
};
/// @brief Structure voxel fragments and metadata

View File

@ -31,6 +31,8 @@ WorldGenerator::WorldGenerator(
seed(seed),
surroundMap(0, BASIC_PROTOTYPE_LAYERS + def.wideStructsChunksRadius * 2)
{
def.script->initialize(seed);
uint levels = BASIC_PROTOTYPE_LAYERS + def.wideStructsChunksRadius * 2;
surroundMap = SurroundMap(0, levels);
@ -228,7 +230,7 @@ void WorldGenerator::generateStructuresWide(
return;
}
auto placements = def.script->placeStructuresWide(
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, CHUNK_H
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, CHUNK_H
);
placeStructures(placements, prototype, chunkX, chunkZ);
@ -245,7 +247,7 @@ void WorldGenerator::generateStructures(
const auto& heightmap = prototype.heightmap;
auto placements = def.script->placeStructures(
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed,
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D},
heightmap, CHUNK_H
);
placeStructures(placements, prototype, chunkX, chunkZ);
@ -297,7 +299,6 @@ void WorldGenerator::generateBiomes(
auto biomeParams = def.script->generateParameterMaps(
{floordiv(chunkX * CHUNK_W, bpd), floordiv(chunkZ * CHUNK_D, bpd)},
{floordiv(CHUNK_W, bpd)+1, floordiv(CHUNK_D, bpd)+1},
seed,
bpd
);
for (auto index : def.heightmapInputs) {
@ -339,7 +340,6 @@ void WorldGenerator::generateHeightmap(
prototype.heightmap = def.script->generateHeightmap(
{floordiv(chunkX * CHUNK_W, bpd), floordiv(chunkZ * CHUNK_D, bpd)},
{floordiv(CHUNK_W, bpd)+1, floordiv(CHUNK_D, bpd)+1},
seed,
bpd,
prototype.heightmapInputs
);