This commit is contained in:
@clasher113 2024-11-04 22:15:56 +02:00
commit bb96db8d55
153 changed files with 1990 additions and 759 deletions

View File

@ -37,7 +37,7 @@ jobs:
run: ./dev/fix_dylibs.sh VoxelEngine Release build run: ./dev/fix_dylibs.sh VoxelEngine Release build
- name: Run tests - name: Run tests
run: ctest --test-dir build run: ctest --output-on-failure --test-dir build
- name: Create DMG - name: Create DMG
run: | run: |

4
.gitignore vendored
View File

@ -48,3 +48,7 @@ appimage-build/
/res/content/* /res/content/*
!/res/content/base !/res/content/base
*.mtl *.mtl
# libs
/libs/
/vcpkg_installed/

View File

@ -70,6 +70,10 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs")
endif() endif()
if (WIN32)
target_link_libraries(${PROJECT_NAME} VoxelEngineSrc winmm)
endif()
target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS}) target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS})
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@ -12,6 +12,12 @@ Icon type defines a source of an item image displayed in inventory.
- **items** (generated from *png* files in *res/textures/items/*) - **items** (generated from *png* files in *res/textures/items/*)
- **block** - block preview. Block ID must be specified in **icon** property. Example: *base:wood*. - **block** - block preview. Block ID must be specified in **icon** property. Example: *base:wood*.
### Item model - `model-name`
Name of the item model. The model will be loaded automatically.
Default value is `packid:itemname.model`.
If the model is not specified, an automatic one will be generated.
## Behaviour ## Behaviour
### *placing-block* ### *placing-block*

View File

@ -21,6 +21,7 @@ Subsections:
- [player](scripting/builtins/libplayer.md) - [player](scripting/builtins/libplayer.md)
- [quat](scripting/builtins/libquat.md) - [quat](scripting/builtins/libquat.md)
- [time](scripting/builtins/libtime.md) - [time](scripting/builtins/libtime.md)
- [utf8](scripting/builtins/libutf8.md)
- [vec2, vec3, vec4](scripting/builtins/libvecn.md) - [vec2, vec3, vec4](scripting/builtins/libvecn.md)
- [world](scripting/builtins/libworld.md) - [world](scripting/builtins/libworld.md)
- [Module core:bit_converter](scripting/modules/core_bit_converter.md) - [Module core:bit_converter](scripting/modules/core_bit_converter.md)

View File

@ -23,6 +23,9 @@ entities.exists(uid: int) -> bool
-- Returns entity definition index by UID -- Returns entity definition index by UID
entities.get_def(uid: int) -> int entities.get_def(uid: int) -> int
-- Returns entity 'hitbox' property value
entities.def_hitbox(id: int) -> vec3
-- Returns entity definition name by index (string ID). -- Returns entity definition name by index (string ID).
entities.def_name(id: int) -> str entities.def_name(id: int) -> str

View File

@ -18,4 +18,13 @@ item.defs_count() -> int
-- Returns item icon name to use in 'src' property of an image element -- Returns item icon name to use in 'src' property of an image element
item.icon(itemid: int) -> str item.icon(itemid: int) -> str
-- Returns the integer id 'placing-block' or 0
item.placing_block(itemid: int) -> int
-- Returns the value of the `model-name` property
item.model_name(itemid: int) -> str
-- Returns item emission property value
item.emission(itemid: int) -> str
``` ```

View File

@ -0,0 +1,27 @@
# *utf8* library
The library provides functions for working with UTF-8.
```lua
-- Converts a UTF-8 string to a Bytearray or an array of numbers if
-- the second argument is true
utf8.tobytes(text: str, [optional] usetable=false) -> Bytearray|table
-- Converts a Bytearray or an array of numbers to a UTF-8 string
utf8.tostring(bytes: Bytearray|table) -> str
-- Returns the length of a Unicode string
utf8.length(text: str) -> int
-- Returns the code of the first character of the string
utf8.codepoint(chars: str) -> int
-- Returns a substring from position startchar to endchar inclusive
utf8.sub(text: str, startchar: int, [optional] endchar: int) -> str
-- Converts a string to uppercase
utf8.upper(text: str) -> str
-- Converts a string to lowercase
utf8.lower(text: str) -> str
```

View File

@ -1,6 +1,7 @@
# *world* library # *world* library
```lua ```lua
-- Returns worlds information.
world.get_list() -> tables array { world.get_list() -> tables array {
-- world name -- world name
name: str, name: str,
@ -27,6 +28,9 @@ world.get_total_time() -> number
-- Returns world seed. -- Returns world seed.
world.get_seed() -> int world.get_seed() -> int
-- Returns generator name.
world.get_generator() -> str
-- Proves that this is the current time during the day -- Proves that this is the current time during the day
-- from 0.333(8 am) to 0.833(8 pm). -- from 0.333(8 am) to 0.833(8 pm).
world.is_day() -> boolean world.is_day() -> boolean

View File

@ -136,3 +136,30 @@ function on_hud_close(playerid: int)
``` ```
Called on world close (before saving) Called on world close (before saving)
## *events* library
```lua
events.on(code: str, handler: function)
```
Adds an event handler by its code, not limited to the standard ones.
```lua
events.reset(code: str, [optional] handler: function)
```
Removes the event, adding a handler if specified.
```lua
events.emit(code: str, args...) -> bool
```
Emits an event by code. If the event does not exist, nothing will happen.
The existence of an event is determined by the presence of handlers.
```lua
events.remove_by_prefix(packid: str)
```
Removes all events with the prefix `packid:`. When you exit the world, events from all packs are unloaded, including `core:`.

View File

@ -66,6 +66,18 @@ input.get_binding_text(bindname: str) -> str
Returns text representation of button by binding name. Returns text representation of button by binding name.
```python
input.is_active(bindname: str) -> bool
```
Checks if the binding is active.
```python
input.set_enabled(bindname: str, flag: bool)
```
Enables/disables binding until leaving the world.
```python ```python
input.is_pressed(code: str) -> bool input.is_pressed(code: str) -> bool
``` ```

View File

@ -72,15 +72,13 @@ Fragments used by the generator must present in the directory:
## Structures ## 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: 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.toml` file. Example:
```lua ```toml
{ tree0 = {}
"tree0": {}, tree1 = {}
"tree1": {}, tree2 = {}
"tree2": {}, tower = {}
"tower": {}, coal_ore0 = {}
"coal_ore0": {}
}
``` ```
Currently, the name of the structure must match the name of the fragment used. Currently, the name of the structure must match the name of the fragment used.
@ -136,7 +134,7 @@ structures = [
- block - plant block - block - plant block
- structure-chance - probability of generating a small structure on a surface block. - structure-chance - probability of generating a small structure on a surface block.
- structures - structures randomly placed on the surface. - structures - structures randomly placed on the surface.
- name - name of the structure declared in `structures.json`. - name - name of the structure declared in `structures.toml`.
- weight - weight directly affecting the chance of choosing a specific structure. - weight - weight directly affecting the chance of choosing a specific structure.
### Biome Parameters ### Biome Parameters
@ -301,6 +299,7 @@ Changes the heightmap size.
Available interpolation modes: Available interpolation modes:
- 'nearest' - no interpolation - 'nearest' - no interpolation
- 'linear' - bilinear interpolation - 'linear' - bilinear interpolation
- 'cubic' - bicubic interpolation
### heightmap:crop(...) ### heightmap:crop(...)
@ -352,7 +351,15 @@ generation.save_fragment(
The fragment size is available as the `size` property. 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. ### Methods
```lua
-- Crop a fragment to content
fragment:crop()
-- Set a fragment to the world at the specified position
fragment:place(position: vec3, [optional] rotation:int=0)
```
## Generating a height map ## Generating a height map

View File

@ -99,6 +99,7 @@ Inner text - initially entered text
- `placeholder` - placeholder text (used if the text field is empty) - `placeholder` - placeholder text (used if the text field is empty)
- `supplier` - text supplier (called every frame) - `supplier` - text supplier (called every frame)
- `consumer` - lua function that receives the entered text. Called only when input is complete - `consumer` - lua function that receives the entered text. Called only when input is complete
- `sub-consumer` - lua function-receiver of the input text. Called during text input or deletion.
- `autoresize` - automatic change of element size (default - false). Does not affect font size. - `autoresize` - automatic change of element size (default - false). Does not affect font size.
- `multiline` - allows display of multiline text. - `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true") - `text-wrap` - allows automatic text wrapping (works only with multiline: "true")

View File

@ -99,4 +99,4 @@
| save-skeleton-pose | поза скелета сущности | false | | save-skeleton-pose | поза скелета сущности | false |
| save-skeleton-textures | динамически назначенные текстуры | false | | save-skeleton-textures | динамически назначенные текстуры | false |
| save-body-velocity | скорость движения тела | true | | save-body-velocity | скорость движения тела | true |
| save-body-settings | измененные настройки тела <br>(type, damping, crouching) | false | | save-body-settings | измененные настройки тела <br>(type, damping, crouching) | true |

View File

@ -11,6 +11,12 @@
- items (генерируется из png файлов в `res/textures/items/`) - items (генерируется из png файлов в `res/textures/items/`)
- `block` - отображает предпросмотр блока. В icon указывается строковый id блока который нужно отображать. Пример `base:wood` - `block` - отображает предпросмотр блока. В icon указывается строковый id блока который нужно отображать. Пример `base:wood`
### Модель предмета - `model-name`
Имя модели предмета. Модель будет загружена автоматически.
Значение по-умолчанию - `packid:itemname.model`.
Если модель не указана, будет сгенерирована автоматическию
## Поведение ## Поведение
### Устанавливаемый блок - `placing-block` ### Устанавливаемый блок - `placing-block`

View File

@ -21,6 +21,7 @@
- [player](scripting/builtins/libplayer.md) - [player](scripting/builtins/libplayer.md)
- [quat](scripting/builtins/libquat.md) - [quat](scripting/builtins/libquat.md)
- [time](scripting/builtins/libtime.md) - [time](scripting/builtins/libtime.md)
- [utf8](scripting/builtins/libutf8.md)
- [vec2, vec3, vec4](scripting/builtins/libvecn.md) - [vec2, vec3, vec4](scripting/builtins/libvecn.md)
- [world](scripting/builtins/libworld.md) - [world](scripting/builtins/libworld.md)
- [Модуль core:bit_converter](scripting/modules/core_bit_converter.md) - [Модуль core:bit_converter](scripting/modules/core_bit_converter.md)

View File

@ -26,6 +26,9 @@ entities.get_def(uid: int) -> int
-- Возвращает имя определения сущности по индексу (строковый ID). -- Возвращает имя определения сущности по индексу (строковый ID).
entities.def_name(id: int) -> str entities.def_name(id: int) -> str
-- Возвращает значение свойства 'hitbox' сущности
entities.def_hitbox(id: int) -> vec3
-- Возвращает индекс определения сущности по имени (числовой ID). -- Возвращает индекс определения сущности по имени (числовой ID).
entities.def_index(name: str) -> int entities.def_index(name: str) -> int

View File

@ -8,7 +8,7 @@ item.name(itemid: int) -> str
item.index(name: str) -> int item.index(name: str) -> int
-- Возвращает название предмета, отображаемое в интерфейсе. -- Возвращает название предмета, отображаемое в интерфейсе.
item.caption(blockid: int) -> str item.caption(itemid: int) -> str
-- Возвращает максимальный размер стопки для предмета. -- Возвращает максимальный размер стопки для предмета.
item.stack_size(itemid: int) -> int item.stack_size(itemid: int) -> int
@ -18,6 +18,15 @@ item.defs_count() -> int
-- Возвращает имя иконки предмета для использования в свойстве 'src' элемента image -- Возвращает имя иконки предмета для использования в свойстве 'src' элемента image
item.icon(itemid: int) -> str item.icon(itemid: int) -> str
-- Возвращает числовой id блока, назначенного как 'placing-block' или 0
item.placing_block(itemid: int) -> int
-- Возвращает значение свойства `model-name`
item.model_name(itemid: int) -> str
-- Возвращает emission параметр у предмета
item.emission(itemid: int) -> str
``` ```

View File

@ -31,7 +31,7 @@ file.write(pack.shared_file(PACK_ID, "example.txt"), text)
``` ```
Для пака *containermod* запишет текст в файл `config:containermod/example.txt` Для пака *containermod* запишет текст в файл `config:containermod/example.txt`
Используйте для хранения данныхm общих для всех миров. Используйте для хранения данных общих для всех миров.
```python ```python
pack.get_folder(packid: str) -> str pack.get_folder(packid: str) -> str

View File

@ -0,0 +1,27 @@
# Библиотека *utf8*
Библиотека предоставляет функции для работы с UTF-8.
```lua
-- Конвертирует UTF-8 строку в Bytearray или массив чисел если
-- второй аргумент - true
utf8.tobytes(text: str, [опционально] usetable=false) -> Bytearray|table
-- Конвертирует Bytearray или массив чисел в UTF-8 строку
utf8.tostring(bytes: Bytearray|table) -> str
-- Возвращает длину юникод-строки
utf8.length(text: str) -> int
-- Возвращает код первого символа строки
utf8.codepoint(chars: str) -> int
-- Возвращает подстроку от позиции startchar до endchar включительно
utf8.sub(text: str, startchar: int, [опционально] endchar: int) -> str
-- Переводит строку в вверхний регистр
utf8.upper(text: str) -> str
-- Переводит строку в нижний регистр
utf8.lower(text: str) -> str
```

View File

@ -27,6 +27,9 @@ world.get_total_time() -> number
-- Возвращает зерно мира. -- Возвращает зерно мира.
world.get_seed() -> int world.get_seed() -> int
-- Возвращает имя генератора.
world.get_generator() -> str
-- Проверяет существование мира по имени. -- Проверяет существование мира по имени.
world.exists() -> bool world.exists() -> bool

View File

@ -135,3 +135,30 @@ function on_hud_close(playerid: int)
``` ```
Вызывается при выходе из мира, перед его сохранением. Вызывается при выходе из мира, перед его сохранением.
## Библиотека *events*
```lua
events.on(code: str, handler: function)
```
Добавляет обработчик события по его коду, не ограничиваясь стандартными.
```lua
events.reset(code: str, [опционально] handler: function)
```
Удаляет событие, добавляя обработчик, если указан.
```lua
events.emit(code: str, args...) -> bool
```
Генерирует событие по коду. Если событие не существует, ничего не произойдет.
Существование события определяется наличием обработчиков.
```lua
events.remove_by_prefix(packid: str)
```
Удаляет все события с префиксом `packid:`. Вы выходе из мира выгружаются события всех паков, включая `core:`.

View File

@ -70,6 +70,12 @@ input.is_active(bindname: str) -> bool
Проверяет активность привязки. Проверяет активность привязки.
```python
input.set_enabled(bindname: str, flag: bool)
```
Включает/выключает привязку до выхода из мира.
```python ```python
input.is_pressed(code: str) -> bool input.is_pressed(code: str) -> bool
``` ```

View File

@ -72,15 +72,13 @@
## Структуры ## Структуры
Структура - набор правил по вставке фрагмента в мир генератором. На данный момент не имеет свойств, создаваясь в виде пустых объектов в файле `generators/имя_генератора.files/structures.json`. Пример: Структура - набор правил по вставке фрагмента в мир генератором. На данный момент не имеет свойств, создаваясь в виде пустых объектов в файле `generators/имя_генератора.files/structures.toml`. Пример:
```lua ```toml
{ tree0 = {}
"tree0": {}, tree1 = {}
"tree1": {}, tree2 = {}
"tree2": {}, tower = {}
"tower": {}, coal_ore0 = {}
"coal_ore0": {}
}
``` ```
На данный момент, имя структуры должно совпадать с именем использованного фрагмента. На данный момент, имя структуры должно совпадать с именем использованного фрагмента.
@ -136,7 +134,7 @@ structures = [
- block - блок растения - block - блок растения
- structure-chance - вероятность генерации малой структуры на блоке поверхности. - structure-chance - вероятность генерации малой структуры на блоке поверхности.
- structures - структуры, случайно расставляемые на поверхности. - structures - структуры, случайно расставляемые на поверхности.
- name - имя структуры, объявленной в `structures.json`. - name - имя структуры, объявленной в `structures.toml`.
- weight - вес, напрямую влияющий на шанс выбора конкретной структуры. - weight - вес, напрямую влияющий на шанс выбора конкретной структуры.
### Параметры биомов ### Параметры биомов
@ -305,6 +303,7 @@ map:resize(ширина, высота, интерполяция)
Доступные режимы интерполяции: Доступные режимы интерполяции:
- 'nearest' - без интерполяции - 'nearest' - без интерполяции
- 'linear' - билинейная интерполяция - 'linear' - билинейная интерполяция
- 'cubic' - бикубическая интерполяция
### heightmap:crop(...) ### heightmap:crop(...)
@ -356,7 +355,15 @@ generation.save_fragment(
Размер фрагмента доступен как свойство `size`. Размер фрагмента доступен как свойство `size`.
Фрагмент может быть обрезан до размеров содержимого (воздух игнорируется) вызовом метода `fragment:crop()`. ### Методы
```lua
-- Обрезает фрагмент до размеров содержимого
fragment:crop()
-- Устанавливает фрагмент в мир на указанной позиции
fragment:place(position: vec3, [опционально] rotation:int=0)
```
## Генерация карты высот ## Генерация карты высот

View File

@ -100,6 +100,7 @@
- `placeholder` - текст подстановки (используется если текстовое поле пусто) - `placeholder` - текст подстановки (используется если текстовое поле пусто)
- `supplier` - поставщик текста (вызывается каждый кадр) - `supplier` - поставщик текста (вызывается каждый кадр)
- `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода - `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода
- `sub-consumer` - lua функция-приемник вводимого текста. Вызывается во время ввода или удаления текста.
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. - `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
- `multiline` - разрешает отображение многострочного текста. - `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")

View File

@ -0,0 +1,109 @@
# VEC3 format specification
3D models binary format.
Byteorder: little-endian
## Syntax
```cpp
enum AttributeType:uint8 {
POSITION = 0,
UV,
NORMAL,
COLOR,
};
sizeof(AttributeType) == 1;
struct VertexAttribute {
AttributeType type; // data type is infered from attribute type
uint8 flags;
uint32 size;
float data[]; // if compressed, first 4 bytes of compressed data is decompressed size
};
sizeof(VertexAttribute) == 6; // + dynamic data array
struct Mesh {
uint32 triangle_count; // number of mesh triangles
uint16 material_id;
uint16 flags;
uint16 attribute_count;
VertexAttribute attributes[];
uint8 indices[]; // if compressed, first 4 bytes of compressed data is compressed buffer size
};
sizeof(Mesh) == 10; // + dynamic attributes array + dynamic indices array
struct Model {
uint16 name_len;
vec3 origin;
uint32 mesh_count;
Mesh meshes[];
char name[];
};
sizeof(Model) == 18; // + dynamic Mesh array + name length
struct Material {
uint16 flags;
uint16 name_len;
char name[];
};
sizeof(Material) == 4; // + dynamic sized string
struct Header {
char[8] ident; // "\0\0VEC3\0\0"
uint16 version; // current is 1
uint16 reserved; // 0x0000
};
sizeof(Header) == 12;
struct Body {
uint16 material_count
uint16 model_count
Material materials[];
Model models[];
};
sizeof(Body) == 4; // + dynamic models array + dynamic materials array
```
\* vertex data: positions are global. Model origins used to make it local.
vertex - is a set of vertex data section entries indices divided by stride, starting from 0 (section first entry).
Example: in file having sections (coordinates, texture_coordinates, normal) vertex is a set of 3 indices ordered the
same way as sections stored in the file.
## Vertex Data section tags
All sections are optional.
| Value | Name | Stride (bytes) | Description |
| ----- | ------------------- | -------------- | --------------------------- |
| %x01 | Coordinates | 12 | vertex position |
| %x02 | Texture coordinates | 8 | vertex texture coordinates |
| %x03 | Normals | 12 | vertex normal vector |
| %x04 | Color | 16 | vertex RGBA color (0.0-1.0) |
VertexAttribute flags:
| Value | Name |
| ----- | ---------------- |
| %x01 | ZLib compression |
## Mesh
Mesh flags:
| Value | Name |
| ----- | ----------------------------------- |
| %x01 | Indices ZLib compression |
| %x02 | Use 16 bit indices instead of 8 bit |
## Material
Material flags:
| Bit offset | Description |
|------------|-------------|
| 0 | Shadeless |
| 1-7 | Reserved |

View File

@ -16,4 +16,5 @@ player.attack="mouse:left"
player.build="mouse:right" player.build="mouse:right"
player.pick="mouse:middle" player.pick="mouse:middle"
player.drop="key:q" player.drop="key:q"
player.fast_interaction="key:x"
hud.inventory="key:tab" hud.inventory="key:tab"

View File

@ -1,5 +1,6 @@
{ {
"texture": "water", "texture": "water",
"overlay-texture": "blocks:water",
"draw-group": 3, "draw-group": 3,
"light-passing": true, "light-passing": true,
"sky-light-passing": false, "sky-light-passing": false,

View File

@ -1,48 +0,0 @@
o Cube
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v -0.5 -0.5 -0.5
v 0.5 0.5 -0.5
v 0.5 0.5 0.5
v -0.5 0.5 0.5
v -0.5 0.5 -0.5
vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 0.0
vt 1.0 0.0
vt 0.0 1.0
vt 0.0 0.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 1.0
vt 1.0 1.0
vt 0.0 1.0
vt 0.0 0.0
vn 0.0 -1.0 0.0
vn 0.0 1.0 0.0
vn 1.0 -0.0 0.0
vn -1.0 -0.0 -0.0
vn 0.0 0.0 -1.0
vn -0.0 -0.0 1.0
usemtl $2
s off
f 1/1/1 2/2/1 3/3/1 4/4/1
usemtl $3
f 5/5/2 8/6/2 7/7/2 6/8/2
usemtl $0
f 1/9/3 5/10/3 6/8/3 2/11/3
usemtl $1
f 3/12/4 7/7/4 8/13/4 4/14/4
usemtl $4
f 5/15/5 1/1/5 4/16/5 8/17/5
usemtl $5
f 2/2/6 6/18/6 7/19/6 3/20/6

View File

@ -1,48 +0,0 @@
o Cube
v 0.125 -0.125 -0.125
v 0.125 -0.125 0.125
v -0.125 -0.125 0.125
v -0.125 -0.125 -0.125
v 0.125 0.125 -0.125
v 0.125 0.125 0.125
v -0.125 0.125 0.125
v -0.125 0.125 -0.125
vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 0.0
vt 1.0 0.0
vt 0.0 1.0
vt 0.0 0.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 1.0
vt 1.0 1.0
vt 0.0 1.0
vt 0.0 0.0
vn 0.0 -1.0 0.0
vn 0.0 1.0 0.0
vn 1.0 -0.0 0.0
vn -1.0 -0.0 -0.0
vn 0.0 0.0 -1.0
vn -0.0 -0.0 1.0
usemtl $2
s off
f 1/1/1 2/2/1 3/3/1 4/4/1
usemtl $3
f 5/5/2 8/6/2 7/7/2 6/8/2
usemtl $0
f 1/9/3 5/10/3 6/8/3 2/11/3
usemtl $1
f 3/12/4 7/7/4 8/13/4 4/14/4
usemtl $4
f 5/15/5 1/1/5 4/16/5 8/17/5
usemtl $5
f 2/2/6 6/18/6 7/19/6 3/20/6

Binary file not shown.

View File

@ -1,113 +0,0 @@
o Cube
v 0.282501 -0.000054 -0.282500
v -0.282501 -0.000054 -0.282501
v -0.282501 -0.000054 0.282500
v 0.282500 -0.000054 0.282501
v 0.282501 0.012502 -0.282500
v -0.282501 0.012502 -0.282501
v -0.282501 0.012502 0.282500
v 0.282500 0.012502 0.282501
v 0.282501 0.012502 -0.282500
v 0.282500 0.012502 0.282501
v -0.282501 0.012502 0.282500
v -0.282501 0.012502 -0.282501
v 0.282501 -0.000054 -0.282500
v 0.282500 -0.000054 0.282501
v -0.282501 -0.000054 0.282500
v -0.282501 -0.000054 -0.282501
v 0.282501 0.012502 -0.282500
v -0.282501 0.012502 -0.282501
v -0.282501 0.012502 0.282500
v 0.282500 0.012502 0.282501
v 0.282501 0.012502 -0.282500
v 0.282500 0.012502 0.282501
v -0.282501 0.012502 0.282500
v -0.282501 0.012502 -0.282501
v 0.282501 -0.015821 -0.282500
v -0.282501 -0.015821 -0.282501
v -0.282501 -0.015821 0.282500
v 0.282500 -0.015821 0.282501
v 0.282501 0.027439 -0.282500
v -0.282501 0.027439 -0.282501
v -0.282501 0.027439 0.282500
v 0.282500 0.027439 0.282501
v 0.282501 0.027439 -0.282500
v 0.282500 0.027439 0.282501
v -0.282501 0.027439 0.282500
v -0.282501 0.027439 -0.282501
v 0.282501 -0.015821 -0.282500
v 0.282500 -0.015821 0.282501
v -0.282501 -0.015821 0.282500
v -0.282501 -0.015821 -0.282501
v 0.282501 0.027439 -0.282500
v -0.282501 0.027439 -0.282501
v -0.282501 0.027439 0.282500
v 0.282500 0.027439 0.282501
v 0.282501 0.027439 -0.282500
v 0.282500 0.027439 0.282501
v -0.282501 0.027439 0.282500
v -0.282501 0.027439 -0.282501
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vn -0.0000 1.0000 0.0000
vn 0.0000 -1.0000 -0.0000
usemtl $0
s 1
f 1/1/1 2/2/1 3/3/1 4/4/1
f 5/5/1 6/6/1 7/7/1 8/8/1
f 9/9/2 10/10/2 11/11/2 12/12/2
f 13/13/2 14/14/2 15/15/2 16/16/2
f 17/17/1 18/18/1 19/19/1 20/20/1
f 21/21/2 22/22/2 23/23/2 24/24/2
f 25/25/1 26/26/1 27/27/1 28/28/1
f 29/29/1 30/30/1 31/31/1 32/32/1
f 33/33/2 34/34/2 35/35/2 36/36/2
f 37/37/2 38/38/2 39/39/2 40/40/2
f 41/41/1 42/42/1 43/43/1 44/44/1
f 45/45/2 46/46/2 47/47/2 48/48/2

View File

@ -1,47 +0,0 @@
# Blender v2.79 (sub 0) OBJ File: 'player.blend'
# www.blender.org
mtllib player-body.mtl
o Cube.001
v -0.125000 -0.900000 0.070903
v -0.125000 -0.900000 -0.070903
v 0.125000 -0.900000 -0.070903
v 0.125000 -0.900000 0.070903
v -0.125000 0.491919 0.070903
v 0.125000 0.491919 0.070903
v 0.125000 0.491919 -0.070903
v -0.125000 0.491919 -0.070903
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.112175 0.434439
vt 0.311556 0.434439
vt 0.311556 0.633819
vt 0.112175 0.633819
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
vn -1.0000 0.0000 0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
usemtl entities/player
s 1
f 1/1/1 2/2/1 3/3/1 4/4/1
f 5/5/2 6/6/2 7/7/2 8/8/2
f 1/1/3 5/9/3 8/10/3 2/11/3
f 2/12/4 8/13/4 7/14/4 3/15/4
f 3/16/5 7/17/5 6/18/5 4/4/5
f 5/5/6 1/19/6 4/20/6 6/21/6

Binary file not shown.

View File

@ -1,42 +0,0 @@
# Blender v2.79 (sub 0) OBJ File: 'player.blend'
# www.blender.org
mtllib player-hand.mtl
o Cube.000_Cube.002
v 0.062480 -0.613786 -0.062480
v 0.062480 -0.613786 0.062480
v -0.062480 -0.613786 0.062480
v -0.062480 -0.613786 -0.062480
v 0.062480 0.070352 -0.062480
v -0.062480 0.070352 -0.062480
v -0.062480 0.070352 0.062480
v 0.062480 0.070352 0.062480
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.436482 0.280393
vt 0.433740 0.937829
vt 0.436146 0.914519
vt 0.438665 0.292591
vt 0.492515 0.918221
vt 0.493194 0.293103
vt 0.493371 0.941872
vt 0.494058 0.280870
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 -0.0000
vn 1.0000 -0.0000 0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl entities/player
s 1
f 1/1/1 2/2/1 3/3/1 4/4/1
f 5/5/2 6/6/2 7/7/2 8/8/2
f 1/9/3 5/10/3 8/11/3 2/12/3
f 2/12/4 8/11/4 7/13/4 3/14/4
f 3/14/5 7/13/5 6/15/5 4/16/5
f 5/10/6 1/9/6 4/16/6 6/15/6

Binary file not shown.

View File

@ -1,48 +0,0 @@
# Blender v2.79 (sub 0) OBJ File: 'player.blend'
# www.blender.org
mtllib player-head.mtl
o Cube.002_Cube.003
v -0.206512 0.031837 0.206512
v -0.206512 0.444861 0.206512
v -0.206512 0.444861 -0.206512
v -0.206512 0.031837 -0.206512
v 0.206512 0.444861 -0.206512
v 0.206512 0.031837 -0.206512
v 0.206512 0.444861 0.206512
v 0.206512 0.031837 0.206512
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.735873 0.213345
vt 0.735873 0.739780
vt 0.209439 0.739780
vt 0.209439 0.213345
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.209065
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.982503 0.209065
vt 0.783122 0.009685
vt 0.982503 0.009685
vt 0.783122 0.209065
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn -0.0000 -0.0000 1.0000
vn 0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 0.0000
usemtl entities/player
s 1
f 1/1/1 2/2/1 3/3/1 4/4/1
f 4/5/2 3/6/2 5/7/2 6/8/2
f 6/9/3 5/10/3 7/11/3 8/12/3
f 8/13/4 7/14/4 2/15/4 1/16/4
f 4/17/5 6/18/5 8/19/5 1/16/5
f 5/20/6 3/21/6 2/15/6 7/22/6

Binary file not shown.

View File

@ -1,5 +1,3 @@
local item_models = require "core:item_models"
local tsf = entity.transform local tsf = entity.transform
local body = entity.rigidbody local body = entity.rigidbody
local rig = entity.skeleton local rig = entity.skeleton
@ -27,7 +25,7 @@ end
do -- setup visuals do -- setup visuals
local matrix = mat4.idt() local matrix = mat4.idt()
scale = item_models.setup(dropitem.id, rig, 0) rig:set_model(0, item.model_name(dropitem.id))
local bodysize = math.min(scale[1], scale[2], scale[3]) * DROP_SCALE local bodysize = math.min(scale[1], scale[2], scale[3]) * DROP_SCALE
body:set_size({scale[1] * DROP_SCALE, bodysize, scale[3] * DROP_SCALE}) body:set_size({scale[1] * DROP_SCALE, bodysize, scale[3] * DROP_SCALE})
mat4.mul(matrix, rotation, matrix) mat4.mul(matrix, rotation, matrix)
@ -38,9 +36,7 @@ end
function on_grounded(force) function on_grounded(force)
local matrix = mat4.idt() local matrix = mat4.idt()
mat4.rotate(matrix, {0, 1, 0}, math.random()*360, matrix) mat4.rotate(matrix, {0, 1, 0}, math.random()*360, matrix)
if model == "aabb" then
mat4.rotate(matrix, {1, 0, 0}, 90, matrix) mat4.rotate(matrix, {1, 0, 0}, 90, matrix)
end
mat4.scale(matrix, scale, matrix) mat4.scale(matrix, scale, matrix)
rig:set_matrix(0, matrix) rig:set_matrix(0, matrix)
inair = false inair = false

View File

@ -1,5 +1,3 @@
local item_models = require "core:item_models"
local tsf = entity.transform local tsf = entity.transform
local body = entity.rigidbody local body = entity.rigidbody
local rig = entity.skeleton local rig = entity.skeleton
@ -9,12 +7,8 @@ local itemIndex = rig:index("item")
local function refresh_model(id) local function refresh_model(id)
itemid = id itemid = id
if id == 0 then rig:set_model(itemIndex, item.model_name(itemid))
rig:set_model(itemIndex, "") rig:set_matrix(itemIndex, mat4.rotate({0, 1, 0}, -80))
else
local scale = item_models.setup(itemid, rig, itemIndex)
rig:set_matrix(itemIndex, mat4.scale(scale))
end
end end
function on_render() function on_render()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -11,6 +11,13 @@
gravity="bottom-left" gravity="bottom-left"
></textbox> ></textbox>
</container> </container>
<panel id="problemsLog"
color="#00000010"
position-func="gui.get_viewport()[1]-350,0"
size-func="351,gui.get_viewport()[2]-40"
padding="5,15,5,15">
<label>@Problems</label>
</panel>
<textbox id='prompt' <textbox id='prompt'
consumer='submit' consumer='submit'
margin='0' margin='0'

View File

@ -1,6 +1,14 @@
history = session.get_entry("commands_history") history = session.get_entry("commands_history")
history_pointer = #history history_pointer = #history
local warning_id = 0
events.on("core:warning", function (wtype, text)
document.problemsLog:add(gui.template("problem", {
type="warning", text=wtype..": "..text, id=tostring(warning_id)
}))
warning_id = warning_id + 1
end)
function setup_variables() function setup_variables()
local pid = hud.get_player() local pid = hud.get_player()
local x,y,z = player.get_pos(pid) local x,y,z = player.get_pos(pid)
@ -27,7 +35,7 @@ function on_history_up()
end end
function on_history_down() function on_history_down()
if history_pointer == #history-1 then if history_pointer >= #history-1 then
return return
end end
history_pointer = history_pointer + 1 history_pointer = history_pointer + 1

View File

@ -3,6 +3,7 @@
<!-- content is generated in script --> <!-- content is generated in script -->
</panel> </panel>
<button pos='15,430' size='440,40' onclick='menu:back()'>@Back</button> <button pos='15,430' size='440,40' onclick='menu:back()'>@Back</button>
<button pos='460,430' size='440,40' onclick='core.open_folder("user:content")'>@Open content folder</button>
<panel id='content_info' pos='485,15' size='440,406' color='0' max-length='406' scrollable='true'> <panel id='content_info' pos='485,15' size='440,406' color='0' max-length='406' scrollable='true'>
<label>@Creator</label> <label>@Creator</label>

View File

@ -10,6 +10,8 @@
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'> <panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
<button onclick='menu.page="languages"' id='langs_btn'>-</button> <button onclick='menu.page="languages"' id='langs_btn'>-</button>
<button onclick='core.open_folder("user:")'>@Open data folder</button>
<button id='s_rst' onclick='set_page("s_rst", "settings_reset")'>@Reset settings</button>
<button onclick='menu:back()'>@Back</button> <button onclick='menu:back()'>@Back</button>
</panel> </panel>
</container> </container>

View File

@ -11,6 +11,7 @@ function set_page(btn, page)
document.s_dsp.enabled = true document.s_dsp.enabled = true
document.s_gfx.enabled = true document.s_gfx.enabled = true
document.s_ctl.enabled = true document.s_ctl.enabled = true
document.s_rst.enabled = true
document[btn].enabled = false document[btn].enabled = false
document.menu.page = page document.menu.page = page
end end

View File

@ -5,7 +5,7 @@
consumer='change_sensitivity'> consumer='change_sensitivity'>
</trackbar> </trackbar>
<panel id='search_panel' size='380,60' padding='2' interval='1' color='#0000004C'> <panel id='search_panel' size='380,60' padding='2' interval='1' color='#0000004C'>
<textbox id='search_textbox' multiline='false' size='300,20' consumer='function(x) refresh_search() end'></textbox> <textbox id='search_textbox' multiline='false' size='300,20' sub-consumer='function(x) refresh_search() end'></textbox>
</panel> </panel>
<panel id='bindings_panel' size='380,204' padding='2' interval='1' max-length='300' color='#0000004C'> <panel id='bindings_panel' size='380,204' padding='2' interval='1' max-length='300' color='#0000004C'>
<!-- content is generated in script --> <!-- content is generated in script -->

View File

@ -59,4 +59,6 @@ function on_open()
create_checkbox("display.fullscreen", "Fullscreen") create_checkbox("display.fullscreen", "Fullscreen")
create_checkbox("camera.shaking", "Camera Shaking") create_checkbox("camera.shaking", "Camera Shaking")
create_checkbox("camera.inertia", "Camera Inertia") create_checkbox("camera.inertia", "Camera Inertia")
create_checkbox("camera.fov-effects", "Camera FOV Effects")
create_checkbox("display.limit-fps-iconified", "Limit Background FPS")
end end

View File

@ -0,0 +1,7 @@
<panel size='380, 0' color='#00000080' padding='8' context='menu'>
<label multiline='false'>@Reset settings</label>
<button onclick='reset("aud")'>@Audio</button>
<button onclick='reset("dsp")'>@Display</button>
<button onclick='reset("gfx")'>@Graphics</button>
<button onclick='reset("ctl")'>@Controls</button>
</panel>

View File

@ -0,0 +1,44 @@
function reset(category)
if category == "aud" then
reset_audio()
elseif category == "dsp" then
reset_display()
elseif category == "gfx" then
reset_graphics()
elseif category == "ctl" then
reset_control()
end
end
function reset_setting(name)
core.set_setting(name, core.get_setting_info(name).def)
end
function reset_audio()
reset_setting("audio.volume-master")
reset_setting("audio.volume-regular")
reset_setting("audio.volume-ui")
reset_setting("audio.volume-ambient")
reset_setting("audio.volume-music")
end
function reset_display()
reset_setting("camera.fov")
reset_setting("display.framerate")
reset_setting("display.fullscreen")
reset_setting("camera.shaking")
reset_setting("camera.inertia")
reset_setting("camera.fov-effects")
end
function reset_graphics()
reset_setting("chunks.load-distance")
reset_setting("chunks.load-speed")
reset_setting("graphics.fog-curve")
reset_setting("graphics.gamma")
reset_setting("graphics.backlight")
end
function reset_control()
input.reset_bindings()
end

View File

@ -0,0 +1,6 @@
<container id="%{id}" size="32" tooltip="%{text}">
<image src="gui/%{type}" size="32"/>
<label pos="36,2">%{text}</label>
<image src="gui/cross" interactive="true" size="16" gravity="top-right"
onclick="document['%{id}']:destruct()"></image>
</container>

BIN
res/models/block.vec3 Normal file

Binary file not shown.

BIN
res/models/drop-item.vec3 Normal file

Binary file not shown.

View File

@ -1,33 +0,0 @@
local function setup(id, rig, index)
rig:set_model(index, "drop-block")
local icon = item.icon(id)
local size = {1.0, 1.0, 1.0}
if icon:find("^block%-previews%:") then
local bid = block.index(icon:sub(16))
model = block.get_model(bid)
if model == "X" then
size = {1.0, 0.3, 1.0}
rig:set_model(index, "drop-item")
rig:set_texture("$0", icon)
else
if model == "aabb" then
local rot = block.get_rotation_profile(bid) == "pipe" and 4 or 0
size = block.get_hitbox(bid, rot)[2]
vec3.mul(size, 2.0, size)
end
local textures = block.get_textures(bid)
for i,t in ipairs(textures) do
rig:set_texture("$"..tostring(i-1), "blocks:"..textures[i])
end
end
else
size = {1.0, 0.3, 1.0}
rig:set_model(index, "drop-item")
rig:set_texture("$0", icon)
end
return size
end
return {
setup=setup,
}

View File

@ -110,19 +110,39 @@ console.add_command(
return "Time set to " .. args[1] return "Time set to " .. args[1]
end end
) )
console.add_command( console.add_command(
"blocks.fill id:str x:num~pos.x y:num~pos.y z:num~pos.z w:int h:int d:int", "time.daycycle operation:[stop|reset]",
"Control time.daycycle",
function(args, kwargs)
local operation = args[1]
if operation == "stop" then
world.set_day_time_speed(0)
return "Daily cycle has stopped"
else
world.set_day_time_speed(1.0)
return "Daily cycle has started"
end
end
)
console.add_command(
"blocks.fill id:str x1:int~pos.x y1:int~pos.y z1:int~pos.z "..
"x2:int~pos.x y2:int~pos.y z2:int~pos.z",
"Fill specified zone with blocks", "Fill specified zone with blocks",
function(args, kwargs) function(args, kwargs)
local name, x, y, z, w, h, d = unpack(args) local name, x1,y1,z1, x2,y2,z2 = unpack(args)
local id = block.index(name) local id = block.index(name)
for ly = 0, h - 1 do for y=y1,y2 do
for lz = 0, d - 1 do for z=z1,z2 do
for lx = 0, w - 1 do for x=x1,x2 do
block.set(x + lx, y + ly, z + lz, id) block.set(x, y, z, id)
end end
end end
end end
local w = math.floor(math.abs(x2-x1+1) + 0.5)
local h = math.floor(math.abs(y2-y1+1) + 0.5)
local d = math.floor(math.abs(z2-z1+1) + 0.5)
return tostring(w * h * d) .. " blocks set" return tostring(w * h * d) .. " blocks set"
end end
) )
@ -151,22 +171,24 @@ console.add_command(
) )
console.add_command( console.add_command(
"fragment.save x:int y:int z:int w:int h:int d:int name:str='untitled' crop:bool=false", "fragment.save x1:int~pos.x y1:int~pos.y z1:int~pos.z "..
"x2:int~pos.x y2:int~pos.y z2:int~pos.z "..
"name:str='untitled' crop:bool=false",
"Save fragment", "Save fragment",
function(args, kwargs) function(args, kwargs)
local x = args[1] local x1 = args[1]
local y = args[2] local y1 = args[2]
local z = args[3] local z1 = args[3]
local w = args[4] local x2 = args[4]
local h = args[5] local y2 = args[5]
local d = args[6] local z2 = args[6]
local name = args[7] local name = args[7]
local crop = args[8] local crop = args[8]
local fragment = generation.create_fragment( local fragment = generation.create_fragment(
{x, y, z}, {x + w, y + h, z + d}, crop, false {x1, y1, z1}, {x2, y2, z2}, crop, false
) )
local filename = 'export:'..name..'.vox' local filename = 'export:'..name..'.vox'
generation.save_fragment(fragment, filename, crop) generation.save_fragment(fragment, filename, crop)
@ -187,3 +209,17 @@ console.add_command(
" has been saved as "..file.resolve(filename)) " has been saved as "..file.resolve(filename))
end end
) )
console.add_command(
"fragment.place file:str x:num~pos.x y:num~pos.y z:num~pos.z rotation:int=0",
"Place fragment to the world",
function(args, kwargs)
local filename = args[1]
local x = args[2]
local y = args[3]
local z = args[4]
local rotation = args[5]
local fragment = generation.load_fragment(filename)
fragment:place({x, y, z}, rotation)
end
)

View File

@ -9,22 +9,36 @@ function sleep(timesec)
end end
end end
-- events ------------------------------------------------
------------------- Events ---------------------
------------------------------------------------
events = { events = {
handlers = {} handlers = {}
} }
function events.on(event, func) function events.on(event, func)
-- why an array? length is always = 1 if events.handlers[event] == nil then
-- FIXME: temporary fixed events.handlers[event] = {}
events.handlers[event] = {} -- events.handlers[event] or {} end
table.insert(events.handlers[event], func) table.insert(events.handlers[event], func)
end end
function events.reset(event, func)
if func == nil then
events.handlers[event] = nil
else
events.handlers[event] = {func}
end
end
function events.remove_by_prefix(prefix) function events.remove_by_prefix(prefix)
for name, handlers in pairs(events.handlers) do for name, handlers in pairs(events.handlers) do
if name:sub(1, #prefix) == prefix then local actualname = name
events.handlers[name] = nil if type(name) == 'table' then
actualname = name[1]
end
if actualname:sub(1, #prefix+1) == prefix..':' then
events.handlers[actualname] = nil
end end
end end
end end
@ -34,11 +48,13 @@ function pack.unload(prefix)
end end
function events.emit(event, ...) function events.emit(event, ...)
result = nil local result = nil
if events.handlers[event] then local handlers = events.handlers[event]
for _, func in ipairs(events.handlers[event]) do if handlers == nil then
result = result or func(...) return nil
end end
for _, func in ipairs(handlers) do
result = result or func(...)
end end
return result return result
end end

View File

@ -109,7 +109,8 @@ function string.explode(separator, str, withpattern)
local current_pos = 1 local current_pos = 1
for i = 1, string_len(str) do for i = 1, string_len(str) do
local start_pos, end_pos = string_find(str, separator, current_pos, not withpattern) local start_pos, end_pos = string_find(
str, separator, current_pos, not withpattern)
if (not start_pos) then break end if (not start_pos) then break end
ret[i] = string_sub(str, current_pos, start_pos - 1) ret[i] = string_sub(str, current_pos, start_pos - 1)
current_pos = end_pos + 1 current_pos = end_pos + 1
@ -139,7 +140,7 @@ function string.formatted_time(seconds, format)
end end
function string.replace(str, tofind, toreplace) function string.replace(str, tofind, toreplace)
local tbl = string.Explode(tofind, str) local tbl = string.explode(tofind, str)
if (tbl[1]) then return table.concat(tbl, toreplace) end if (tbl[1]) then return table.concat(tbl, toreplace) end
return str return str
end end
@ -159,6 +160,9 @@ function string.trim_left(s, char)
return string.match(s, "^" .. char .. "*(.+)$") or s return string.match(s, "^" .. char .. "*(.+)$") or s
end end
string.lower = utf8.lower
string.upper = utf8.upper
local meta = getmetatable("") local meta = getmetatable("")
function meta:__index(key) function meta:__index(key)
@ -234,6 +238,7 @@ function on_deprecated_call(name, alternatives)
return return
end end
__warnings_hidden[name] = true __warnings_hidden[name] = true
events.emit("core:warning", "deprecated call", name)
if alternatives then if alternatives then
debug.warning("deprecated function called ("..name.."), use ".. debug.warning("deprecated function called ("..name.."), use "..
alternatives.." instead\n"..debug.traceback()) alternatives.." instead\n"..debug.traceback())

View File

@ -16,7 +16,7 @@ void main() {
float depth = (a_distance/256.0); float depth = (a_distance/256.0);
float alpha = a_color.a * tex_color.a; float alpha = a_color.a * tex_color.a;
// anyway it's any alpha-test alternative required // anyway it's any alpha-test alternative required
if (alpha < 0.3f) if (alpha < 0.9f)
discard; discard;
f_color = mix(a_color * tex_color, vec4(fogColor,1.0), f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
min(1.0, pow(depth*u_fogFactor, u_fogCurve))); min(1.0, pow(depth*u_fogFactor, u_fogCurve)));

View File

@ -31,6 +31,7 @@ hud.inventory=Inventory
player.pick=Pick Block player.pick=Pick Block
player.attack=Attack / Break player.attack=Attack / Break
player.build=Place Block player.build=Place Block
player.fast_interaction=Accelerated interaction
player.flight=Flight player.flight=Flight
player.noclip=No-clip player.noclip=No-clip
player.drop=Drop Item player.drop=Drop Item

View File

@ -38,7 +38,10 @@ menu.Page not found=Страница не найдена
menu.Quit=Выход menu.Quit=Выход
menu.Save and Quit to Menu=Сохранить и Выйти в Меню menu.Save and Quit to Menu=Сохранить и Выйти в Меню
menu.Settings=Настройки menu.Settings=Настройки
menu.Reset settings=Сбросить настройки
menu.Contents Menu=Меню контентпаков menu.Contents Menu=Меню контентпаков
menu.Open data folder=Открыть папку данных
menu.Open content folder=Открыть папку [content]
world.Seed=Зерно world.Seed=Зерно
world.Name=Название world.Name=Название
@ -57,6 +60,7 @@ settings.Ambient=Фон
settings.Backlight=Подсветка settings.Backlight=Подсветка
settings.Camera Shaking=Тряска Камеры settings.Camera Shaking=Тряска Камеры
settings.Camera Inertia=Инерция Камеры settings.Camera Inertia=Инерция Камеры
settings.Camera FOV Effects=Эффекты поля зрения
settings.Fog Curve=Кривая Тумана settings.Fog Curve=Кривая Тумана
settings.FOV=Поле Зрения settings.FOV=Поле Зрения
settings.Fullscreen=Полный экран settings.Fullscreen=Полный экран
@ -72,6 +76,7 @@ settings.Regular Sounds=Обычные Звуки
settings.UI Sounds=Звуки Интерфейса settings.UI Sounds=Звуки Интерфейса
settings.V-Sync=Вертикальная Синхронизация settings.V-Sync=Вертикальная Синхронизация
settings.Key=Кнопка settings.Key=Кнопка
settings.Limit Background FPS=Ограничить фоновую частоту кадров
# Управление # Управление
chunks.reload=Перезагрузить Чанки chunks.reload=Перезагрузить Чанки
@ -88,6 +93,7 @@ hud.inventory=Инвентарь
player.pick=Подобрать Блок player.pick=Подобрать Блок
player.attack=Атаковать / Сломать player.attack=Атаковать / Сломать
player.build=Поставить Блок player.build=Поставить Блок
player.fast_interaction=Ускоренное взаимодействие
player.flight=Полёт player.flight=Полёт
player.drop=Выбросить Предмет player.drop=Выбросить Предмет
camera.zoom=Приближение camera.zoom=Приближение

View File

@ -5,11 +5,13 @@
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <stdexcept>
#include <typeindex> #include <typeindex>
#include <typeinfo> #include <typeinfo>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "util/stringutil.hpp"
#include "graphics/core/TextureAnimation.hpp" #include "graphics/core/TextureAnimation.hpp"
class Assets; class Assets;
@ -84,6 +86,15 @@ public:
return static_cast<T*>(found->second.get()); return static_cast<T*>(found->second.get());
} }
template <class T>
T& require(const std::string& name) const {
T* asset = get<T>(name);
if (asset == nullptr) {
throw std::runtime_error(util::quote(name) + " not found");
}
return *asset;
}
template <class T> template <class T>
std::optional<const assets_map*> getMap() const { std::optional<const assets_map*> getMap() const {
const auto& mapIter = assets.find(typeid(T)); const auto& mapIter = assets.find(typeid(T));

View File

@ -16,6 +16,7 @@
#include "objects/rigging.hpp" #include "objects/rigging.hpp"
#include "util/ThreadPool.hpp" #include "util/ThreadPool.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "items/ItemDef.hpp"
#include "Assets.hpp" #include "Assets.hpp"
#include "assetload_funcs.hpp" #include "assetload_funcs.hpp"
@ -212,12 +213,6 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.tryAddSound(material.breakSound); loader.tryAddSound(material.breakSound);
} }
addLayouts(
0,
"core",
loader.getPaths()->getMainRoot() / fs::path("layouts"),
loader
);
for (auto& entry : content->getPacks()) { for (auto& entry : content->getPacks()) {
auto pack = entry.second.get(); auto pack = entry.second.get();
auto& info = pack->getInfo(); auto& info = pack->getInfo();
@ -228,7 +223,11 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
for (auto& entry : content->getSkeletons()) { for (auto& entry : content->getSkeletons()) {
auto& skeleton = *entry.second; auto& skeleton = *entry.second;
for (auto& bone : skeleton.getBones()) { for (auto& bone : skeleton.getBones()) {
auto& model = bone->model.name; std::string model = bone->model.name;
size_t pos = model.rfind('.');
if (pos != std::string::npos) {
model = model.substr(0, pos);
}
if (!model.empty()) { if (!model.empty()) {
loader.add( loader.add(
AssetType::MODEL, MODELS_FOLDER + "/" + model, model AssetType::MODEL, MODELS_FOLDER + "/" + model, model
@ -236,6 +235,15 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
} }
} }
} }
for (const auto& [_, def] : content->items.getDefs()) {
if (def->modelName.find(':') == std::string::npos) {
loader.add(
AssetType::MODEL,
MODELS_FOLDER + "/" + def->modelName,
def->modelName
);
}
}
} }
} }

View File

@ -10,6 +10,7 @@
#include "coders/imageio.hpp" #include "coders/imageio.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "coders/obj.hpp" #include "coders/obj.hpp"
#include "coders/vec3.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
#include "files/engine_paths.hpp" #include "files/engine_paths.hpp"
@ -23,6 +24,7 @@
#include "graphics/core/Texture.hpp" #include "graphics/core/Texture.hpp"
#include "graphics/core/TextureAnimation.hpp" #include "graphics/core/TextureAnimation.hpp"
#include "objects/rigging.hpp" #include "objects/rigging.hpp"
#include "util/stringutil.hpp"
#include "Assets.hpp" #include "Assets.hpp"
#include "AssetsLoader.hpp" #include "AssetsLoader.hpp"
@ -39,18 +41,32 @@ static bool animation(
Atlas* dstAtlas Atlas* dstAtlas
); );
assetload::postfunc assetload:: assetload::postfunc assetload::texture(
texture(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr<AssetCfg>&) { AssetsLoader*,
std::shared_ptr<ImageData> image( const ResPaths* paths,
imageio::read(paths->find(filename + ".png").u8string()).release() const std::string& filename,
); const std::string& name,
return [name, image](auto assets) { const std::shared_ptr<AssetCfg>&
) {
auto actualFile = paths->find(filename + ".png").u8string();
try {
std::shared_ptr<ImageData> image(imageio::read(actualFile).release());
return [name, image, actualFile](auto assets) {
assets->store(Texture::from(image.get()), name); assets->store(Texture::from(image.get()), name);
}; };
} catch (const std::runtime_error& err) {
logger.error() << actualFile << ": " << err.what();
return [](auto) {};
}
} }
assetload::postfunc assetload:: assetload::postfunc assetload::shader(
shader(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr<AssetCfg>&) { AssetsLoader*,
const ResPaths* paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
fs::path vertexFile = paths->find(filename + ".glslv"); fs::path vertexFile = paths->find(filename + ".glslv");
fs::path fragmentFile = paths->find(filename + ".glslf"); fs::path fragmentFile = paths->find(filename + ".glslf");
@ -181,8 +197,7 @@ assetload::postfunc assetload::sound(
if (!fs::exists(variantFile)) { if (!fs::exists(variantFile)) {
break; break;
} }
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM) baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
);
} }
auto sound = baseSound.release(); auto sound = baseSound.release();
@ -191,14 +206,8 @@ assetload::postfunc assetload::sound(
}; };
} }
assetload::postfunc assetload:: static void request_textures(AssetsLoader* loader, const model::Model& model) {
model(AssetsLoader* loader, const ResPaths* paths, const std::string& file, const std::string& name, const std::shared_ptr<AssetCfg>&) { for (auto& mesh : model.meshes) {
auto path = paths->find(file + ".obj");
auto text = files::read_string(path);
try {
auto model = obj::parse(path.u8string(), text).release();
return [=](Assets* assets) {
for (auto& mesh : model->meshes) {
if (mesh.texture.find('$') == std::string::npos) { if (mesh.texture.find('$') == std::string::npos) {
auto filename = TEXTURES_FOLDER + "/" + mesh.texture; auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
loader->add( loader->add(
@ -206,6 +215,41 @@ assetload::postfunc assetload::
); );
} }
} }
}
assetload::postfunc assetload::model(
AssetsLoader* loader,
const ResPaths* paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto path = paths->find(file + ".vec3");
if (fs::exists(path)) {
auto bytes = files::read_bytes_buffer(path);
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.u8string(), bytes));
return [loader, name, modelVEC3=std::move(modelVEC3)](Assets* assets) {
for (auto& [modelName, model] : modelVEC3->models) {
request_textures(loader, model.model);
std::string fullName = name;
if (name != modelName) {
fullName += "." + modelName;
}
assets->store(
std::make_unique<model::Model>(model.model),
fullName
);
logger.info() << "store model " << util::quote(modelName)
<< " as " << util::quote(fullName);
}
};
}
path = paths->find(file + ".obj");
auto text = files::read_string(path);
try {
auto model = obj::parse(path.u8string(), text).release();
return [=](Assets* assets) {
request_textures(loader, *model);
assets->store(std::unique_ptr<model::Model>(model), name); assets->store(std::unique_ptr<model::Model>(model), name);
}; };
} catch (const parsing_error& err) { } catch (const parsing_error& err) {

View File

@ -0,0 +1,24 @@
#include "assets_util.hpp"
#include "assets/Assets.hpp"
#include "graphics/core/Atlas.hpp"
#include "graphics/core/Texture.hpp"
util::TextureRegion util::get_texture_region(
const Assets& assets, const std::string& name, const std::string& fallback
) {
size_t sep = name.find(':');
if (sep == std::string::npos) {
return {assets.get<Texture>(name), UVRegion(0,0,1,1)};
} else {
auto atlas = assets.get<Atlas>(name.substr(0, sep));
if (atlas) {
if (auto reg = atlas->getIf(name.substr(sep+1))) {
return {atlas->getTexture(), *reg};
} else if (!fallback.empty()){
return util::get_texture_region(assets, fallback, "");
}
}
}
return {nullptr, UVRegion()};
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include "maths/UVRegion.hpp"
class Assets;
class Texture;
namespace util {
struct TextureRegion {
const Texture* texture;
UVRegion region;
};
TextureRegion get_texture_region(
const Assets& assets,
const std::string& name,
const std::string& fallback
);
}

View File

@ -107,6 +107,14 @@ void ByteReader::checkMagic(const char* data, size_t size) {
pos += size; pos += size;
} }
void ByteReader::get(char* dst, size_t size) {
if (pos + size > this->size) {
throw std::runtime_error("buffer underflow");
}
std::memcpy(dst, data+pos, size);
pos += size;
}
ubyte ByteReader::get() { ubyte ByteReader::get() {
if (pos == size) { if (pos == size) {
throw std::runtime_error("buffer underflow"); throw std::runtime_error("buffer underflow");

View File

@ -52,6 +52,8 @@ public:
ByteReader(const ubyte* data); ByteReader(const ubyte* data);
void checkMagic(const char* data, size_t size); void checkMagic(const char* data, size_t size);
/// @brief Get N bytes
void get(char* dst, size_t size);
/// @brief Read one byte (unsigned 8 bit integer) /// @brief Read one byte (unsigned 8 bit integer)
ubyte get(); ubyte get();
/// @brief Read one byte (unsigned 8 bit integer) without pointer move /// @brief Read one byte (unsigned 8 bit integer) without pointer move

View File

@ -360,6 +360,13 @@ std::string BasicParser::parseString(char quote, bool closeRequired) {
ss << (char)parseSimpleInt(8); ss << (char)parseSimpleInt(8);
continue; continue;
} }
if (c == 'u') {
int codepoint = parseSimpleInt(16);
ubyte bytes[4];
int size = util::encode_utf8(codepoint, bytes);
ss.write(reinterpret_cast<char*>(bytes), size);
continue;
}
switch (c) { switch (c) {
case 'n': ss << '\n'; break; case 'n': ss << '\n'; break;
case 'r': ss << '\r'; break; case 'r': ss << '\r'; break;

230
src/coders/vec3.cpp Normal file
View File

@ -0,0 +1,230 @@
#include "vec3.hpp"
#include <stdexcept>
#include "byte_utils.hpp"
#include "util/data_io.hpp"
#include "util/stringutil.hpp"
#include "graphics/core/Model.hpp"
inline constexpr int VERSION = 1;
inline constexpr int FLAG_ZLIB = 0x1;
inline constexpr int FLAG_16BIT_INDICES = 0x2;
using namespace vec3;
vec3::Model::~Model() = default;
enum AttributeType {
POSITION = 0,
UV,
NORMAL,
COLOR
};
struct VertexAttribute {
AttributeType type;
int flags;
util::Buffer<float> data;
VertexAttribute() = default;
VertexAttribute(VertexAttribute&&) = default;
VertexAttribute& operator=(VertexAttribute&& o) {
type = o.type;
flags = o.flags;
data = std::move(o.data);
return *this;
}
};
static VertexAttribute load_attribute(ByteReader& reader) {
auto type = static_cast<AttributeType>(reader.get());
int flags = reader.get();
assert(type >= POSITION && flags <= COLOR);
if (flags != 0) {
throw std::runtime_error("attribute compression is not supported yet");
}
int size = reader.getInt32();
util::Buffer<float> data(size / sizeof(float));
reader.get(reinterpret_cast<char*>(data.data()), size);
if (dataio::is_big_endian()) {
for (int i = 0; i < data.size(); i++) {
data[i] = dataio::swap(data[i]);
}
}
return VertexAttribute {type, flags, std::move(data)};
}
static model::Mesh build_mesh(
const std::vector<VertexAttribute>& attrs,
const util::Buffer<uint16_t>& indices,
const std::string& texture
) {
const glm::vec3* coords = nullptr;
const glm::vec2* uvs = nullptr;
const glm::vec3* normals = nullptr;
int coordsIndex, uvsIndex, normalsIndex;
for (int i = 0; i < attrs.size(); i++) {
const auto& attr = attrs[i];
switch (attr.type) {
case POSITION:
coords = reinterpret_cast<const glm::vec3*>(attr.data.data());
coordsIndex = i;
break;
case UV:
uvs = reinterpret_cast<const glm::vec2*>(attr.data.data());
uvsIndex = i;
break;
case NORMAL:
normals = reinterpret_cast<const glm::vec3*>(attr.data.data());
normalsIndex = i;
break;
case COLOR: // unused
break;
}
}
std::vector<model::Vertex> vertices;
int attrsCount = attrs.size();
int verticesCount = indices.size() / attrsCount;
for (int i = 0; i < verticesCount; i++) {
model::Vertex vertex {};
if (coords) {
vertex.coord = coords[indices[i * attrsCount + coordsIndex]];
}
if (uvs) {
vertex.uv = uvs[indices[i * attrsCount + uvsIndex]];
}
if (normals) {
vertex.normal = normals[indices[i * attrsCount + normalsIndex]];
} else if (coords) {
// Flat normal calculation
int idx = (i / 3) * 3;
auto a = coords[indices[idx * attrsCount + coordsIndex]];
auto b = coords[indices[(idx + 1) * attrsCount + coordsIndex]];
auto c = coords[indices[(idx + 2) * attrsCount + coordsIndex]];
vertex.normal = glm::normalize(glm::cross(b - a, c - a));
}
vertices.push_back(std::move(vertex));
}
return model::Mesh {texture, std::move(vertices)};
}
static model::Mesh load_mesh(
ByteReader& reader, const std::vector<Material>& materials
) {
int triangleCount = reader.getInt32();
int materialId = reader.getInt16();
int flags = reader.getInt16();
int attributeCount = reader.getInt16();
if (flags == FLAG_ZLIB) {
throw std::runtime_error("compression is not supported yet");
}
std::vector<VertexAttribute> attributes;
for (int i = 0; i < attributeCount; i++) {
attributes.push_back(load_attribute(reader));
}
util::Buffer<uint16_t> indices(triangleCount * 3 * attributeCount);
if ((flags & FLAG_16BIT_INDICES) == 0){
util::Buffer<uint8_t> smallIndices(indices.size());
reader.get(
reinterpret_cast<char*>(smallIndices.data()),
indices.size() * sizeof(uint8_t)
);
for (int i = 0; i < indices.size(); i++) {
indices[i] = smallIndices[i];
}
} else {
reader.get(
reinterpret_cast<char*>(indices.data()),
indices.size() * sizeof(uint16_t)
);
}
if (dataio::is_big_endian()) {
for (int i = 0; i < indices.size(); i++) {
indices[i] = dataio::swap(indices[i]);
}
}
return build_mesh(
attributes,
indices,
materials.at(materialId).name
);
}
static Model load_model(
ByteReader& reader, const std::vector<Material>& materials
) {
int nameLength = reader.getInt16();
assert(nameLength >= 0);
float x = reader.getFloat32();
float y = reader.getFloat32();
float z = reader.getFloat32();
int meshCount = reader.getInt32();
assert(meshCount >= 0);
std::vector<model::Mesh> meshes;
for (int i = 0; i < meshCount; i++) {
meshes.push_back(load_mesh(reader, materials));
}
util::Buffer<char> chars(nameLength);
reader.get(chars.data(), nameLength);
std::string name(chars.data(), nameLength);
glm::vec3 offset {x, y, z};
for (auto& mesh : meshes) {
for (auto& vertex : mesh.vertices) {
vertex.coord -= offset;
}
}
return Model {std::move(name), model::Model {std::move(meshes)}, {x, y, z}};
}
static Material load_material(ByteReader& reader) {
int flags = reader.getInt16();
int nameLength = reader.getInt16();
assert(nameLength >= 0);
util::Buffer<char> chars(nameLength);
reader.get(chars.data(), nameLength);
std::string name(chars.data(), nameLength);
return Material {flags, std::move(name)};
}
File vec3::load(
const std::string_view file, const util::Buffer<ubyte>& src
) {
ByteReader reader(src.data(), src.size());
// Header
reader.checkMagic("\0\0VEC3\0\0", 8);
int version = reader.getInt16();
int reserved = reader.getInt16();
if (version > VERSION) {
throw std::runtime_error("unsupported VEC3 version");
}
assert(reserved == 0);
// Body
int materialCount = reader.getInt16();
int modelCount = reader.getInt16();
assert(materialCount >= 0);
assert(modelCount >= 0);
std::vector<Material> materials;
for (int i = 0; i < materialCount; i++) {
materials.push_back(load_material(reader));
}
std::unordered_map<std::string, Model> models;
for (int i = 0; i < modelCount; i++) {
Model model = load_model(reader, materials);
models[model.name] = std::move(model);
}
return File {std::move(models), std::move(materials)};
}

40
src/coders/vec3.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include <unordered_map>
#include "typedefs.hpp"
#include "util/Buffer.hpp"
#include "graphics/core/Model.hpp"
/// See /doc/specs/vec3_model_spec.md
namespace vec3 {
struct Material {
int flags;
std::string name;
};
struct Model {
std::string name;
model::Model model;
glm::vec3 origin;
Model& operator=(Model&&) = default;
~Model();
};
struct File {
std::unordered_map<std::string, Model> models;
std::vector<Material> materials;
File(File&&) = default;
File& operator=(File&&) = default;
};
File load(const std::string_view file, const util::Buffer<ubyte>& src);
}

View File

@ -44,6 +44,8 @@ std::unique_ptr<Content> ContentBuilder::build() {
def.rt.hitboxes[i].push_back(aabb); def.rt.hitboxes[i].push_back(aabb);
} }
} }
} else {
def.rt.hitboxes->emplace_back(AABB(glm::vec3(1.0f)));
} }
blockDefsIndices.push_back(&def); blockDefsIndices.push_back(&def);

View File

@ -326,6 +326,7 @@ void ContentLoader::loadBlock(
root.at("ui-layout").get(def.uiLayout); root.at("ui-layout").get(def.uiLayout);
root.at("inventory-size").get(def.inventorySize); root.at("inventory-size").get(def.inventorySize);
root.at("tick-interval").get(def.tickInterval); root.at("tick-interval").get(def.tickInterval);
root.at("overlay-texture").get(def.overlayTexture);
if (root.has("fields")) { if (root.has("fields")) {
def.dataStruct = std::make_unique<StructLayout>(); def.dataStruct = std::make_unique<StructLayout>();
@ -418,17 +419,18 @@ void ContentLoader::loadItem(
std::string iconTypeStr = ""; std::string iconTypeStr = "";
root.at("icon-type").get(iconTypeStr); root.at("icon-type").get(iconTypeStr);
if (iconTypeStr == "none") { if (iconTypeStr == "none") {
def.iconType = item_icon_type::none; def.iconType = ItemIconType::NONE;
} else if (iconTypeStr == "block") { } else if (iconTypeStr == "block") {
def.iconType = item_icon_type::block; def.iconType = ItemIconType::BLOCK;
} else if (iconTypeStr == "sprite") { } else if (iconTypeStr == "sprite") {
def.iconType = item_icon_type::sprite; def.iconType = ItemIconType::SPRITE;
} else if (iconTypeStr.length()) { } else if (iconTypeStr.length()) {
logger.error() << name << ": unknown icon type" << iconTypeStr; logger.error() << name << ": unknown icon type" << iconTypeStr;
} }
root.at("icon").get(def.icon); root.at("icon").get(def.icon);
root.at("placing-block").get(def.placingBlock); root.at("placing-block").get(def.placingBlock);
root.at("script-name").get(def.scriptName); root.at("script-name").get(def.scriptName);
root.at("model-name").get(def.modelName);
root.at("stack-size").get(def.stackSize); root.at("stack-size").get(def.stackSize);
// item light emission [r, g, b] where r,g,b in range [0..15] // item light emission [r, g, b] where r,g,b in range [0..15]
@ -532,7 +534,7 @@ void ContentLoader::loadBlock(
auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX); auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX);
item.generated = true; item.generated = true;
item.caption = def.caption; item.caption = def.caption;
item.iconType = item_icon_type::block; item.iconType = ItemIconType::BLOCK;
item.icon = full; item.icon = full;
item.placingBlock = full; item.placingBlock = full;

View File

@ -202,6 +202,16 @@ void ContentLoader::loadGenerator(
map.at("biome-parameters").get(def.biomeParameters); map.at("biome-parameters").get(def.biomeParameters);
map.at("biome-bpd").get(def.biomesBPD); map.at("biome-bpd").get(def.biomesBPD);
map.at("heights-bpd").get(def.heightsBPD); map.at("heights-bpd").get(def.heightsBPD);
std::string interpName;
map.at("heights-interpolation").get(interpName);
if (auto interp = InterpolationType_from(interpName)) {
def.heightsInterpolation = *interp;
}
map.at("biomes-interpolation").get(interpName);
if (auto interp = InterpolationType_from(interpName)) {
def.biomesInterpolation = *interp;
}
map.at("sea-level").get(def.seaLevel); map.at("sea-level").get(def.seaLevel);
map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius); map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius);
if (map.has("heightmap-inputs")) { if (map.has("heightmap-inputs")) {

View File

@ -25,13 +25,13 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
} }
{ {
ItemDef& item = builder->items.create(CORE_EMPTY); ItemDef& item = builder->items.create(CORE_EMPTY);
item.iconType = item_icon_type::none; item.iconType = ItemIconType::NONE;
} }
auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml"); auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml");
if (fs::is_regular_file(bindsFile)) { if (fs::is_regular_file(bindsFile)) {
Events::loadBindings( Events::loadBindings(
bindsFile.u8string(), files::read_string(bindsFile) bindsFile.u8string(), files::read_string(bindsFile), BindType::BIND
); );
} }
@ -43,7 +43,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
block.hitboxes = {AABB()}; block.hitboxes = {AABB()};
block.breakable = false; block.breakable = false;
ItemDef& item = builder->items.create(CORE_OBSTACLE+".item"); ItemDef& item = builder->items.create(CORE_OBSTACLE+".item");
item.iconType = item_icon_type::block; item.iconType = ItemIconType::BLOCK;
item.icon = CORE_OBSTACLE; item.icon = CORE_OBSTACLE;
item.placingBlock = CORE_OBSTACLE; item.placingBlock = CORE_OBSTACLE;
item.caption = block.caption; item.caption = block.caption;
@ -59,7 +59,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
block.hitboxes = {AABB()}; block.hitboxes = {AABB()};
block.obstacle = false; block.obstacle = false;
ItemDef& item = builder->items.create(CORE_STRUCT_AIR+".item"); ItemDef& item = builder->items.create(CORE_STRUCT_AIR+".item");
item.iconType = item_icon_type::block; item.iconType = ItemIconType::BLOCK;
item.icon = CORE_STRUCT_AIR; item.icon = CORE_STRUCT_AIR;
item.placingBlock = CORE_STRUCT_AIR; item.placingBlock = CORE_STRUCT_AIR;
item.caption = block.caption; item.caption = block.caption;

View File

@ -27,6 +27,8 @@ inline const std::string BIND_PLAYER_FLIGHT = "player.flight";
inline const std::string BIND_PLAYER_ATTACK = "player.attack"; inline const std::string BIND_PLAYER_ATTACK = "player.attack";
inline const std::string BIND_PLAYER_BUILD = "player.build"; inline const std::string BIND_PLAYER_BUILD = "player.build";
inline const std::string BIND_PLAYER_PICK = "player.pick"; inline const std::string BIND_PLAYER_PICK = "player.pick";
inline const std::string BIND_PLAYER_FAST_INTERACTOIN =
"player.fast_interaction";
inline const std::string BIND_HUD_INVENTORY = "hud.inventory"; inline const std::string BIND_HUD_INVENTORY = "hud.inventory";
class EnginePaths; class EnginePaths;

View File

@ -57,6 +57,10 @@ public:
return value; return value;
} }
const T& getDefault() const {
return initial;
}
T& operator*() { T& operator*() {
return value; return value;
} }

View File

@ -20,6 +20,7 @@
#include "frontend/menu.hpp" #include "frontend/menu.hpp"
#include "frontend/screens/Screen.hpp" #include "frontend/screens/Screen.hpp"
#include "frontend/screens/MenuScreen.hpp" #include "frontend/screens/MenuScreen.hpp"
#include "graphics/render/ModelsGenerator.hpp"
#include "graphics/core/Batch2D.hpp" #include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
#include "graphics/core/ImageData.hpp" #include "graphics/core/ImageData.hpp"
@ -131,7 +132,7 @@ void Engine::loadControls() {
if (fs::is_regular_file(controls_file)) { if (fs::is_regular_file(controls_file)) {
logger.info() << "loading controls"; logger.info() << "loading controls";
std::string text = files::read_string(controls_file); std::string text = files::read_string(controls_file);
Events::loadBindings(controls_file.u8string(), text); Events::loadBindings(controls_file.u8string(), text, BindType::BIND);
} }
} }
@ -184,8 +185,11 @@ void Engine::mainloop() {
if (!Window::isIconified()) { if (!Window::isIconified()) {
renderFrame(batch); renderFrame(batch);
} }
Window::setFramerate(Window::isIconified() ? 20 : Window::setFramerate(
settings.display.framerate.get()); Window::isIconified() && settings.display.limitFpsIconified.get()
? 20
: settings.display.framerate.get()
);
processPostRunnables(); processPostRunnables();
@ -280,6 +284,17 @@ void Engine::loadAssets() {
} }
} }
assets = std::move(new_assets); assets = std::move(new_assets);
if (content) {
for (auto& [name, def] : content->items.getDefs()) {
assets->store(
std::make_unique<model::Model>(
ModelsGenerator::generate(*def, *content, *assets)
),
name + ".model"
);
}
}
} }
static void load_configs(const fs::path& root) { static void load_configs(const fs::path& root) {
@ -287,12 +302,14 @@ static void load_configs(const fs::path& root) {
auto bindsFile = configFolder/fs::path("bindings.toml"); auto bindsFile = configFolder/fs::path("bindings.toml");
if (fs::is_regular_file(bindsFile)) { if (fs::is_regular_file(bindsFile)) {
Events::loadBindings( Events::loadBindings(
bindsFile.u8string(), files::read_string(bindsFile) bindsFile.u8string(), files::read_string(bindsFile), BindType::BIND
); );
} }
} }
void Engine::loadContent() { void Engine::loadContent() {
scripting::cleanup();
auto resdir = paths->getResourcesFolder(); auto resdir = paths->getResourcesFolder();
std::vector<std::string> names; std::vector<std::string> names;
@ -338,6 +355,7 @@ void Engine::loadContent() {
} }
void Engine::resetContent() { void Engine::resetContent() {
scripting::cleanup();
auto resdir = paths->getResourcesFolder(); auto resdir = paths->getResourcesFolder();
std::vector<PathsRoot> resRoots; std::vector<PathsRoot> resRoots;
{ {
@ -388,6 +406,9 @@ double Engine::getDelta() const {
} }
void Engine::setScreen(std::shared_ptr<Screen> screen) { void Engine::setScreen(std::shared_ptr<Screen> screen) {
// unblock all bindings
Events::enableBindings();
// reset audio channels (stop all sources)
audio::reset_channel(audio::get_channel_index("regular")); audio::reset_channel(audio::get_channel_index("regular"));
audio::reset_channel(audio::get_channel_index("ambient")); audio::reset_channel(audio::get_channel_index("ambient"));
this->screen = std::move(screen); this->screen = std::move(screen);

View File

@ -75,7 +75,11 @@ std::unique_ptr<ubyte[]> files::read_bytes(
const fs::path& filename, size_t& length const fs::path& filename, size_t& length
) { ) {
std::ifstream input(filename, std::ios::binary); std::ifstream input(filename, std::ios::binary);
if (!input.is_open()) return nullptr; if (!input.is_open()) {
throw std::runtime_error(
"could not to load file '" + filename.string() + "'"
);
}
input.seekg(0, std::ios_base::end); input.seekg(0, std::ios_base::end);
length = input.tellg(); length = input.tellg();
input.seekg(0, std::ios_base::beg); input.seekg(0, std::ios_base::beg);
@ -102,16 +106,11 @@ std::vector<ubyte> files::read_bytes(const fs::path& filename) {
std::string files::read_string(const fs::path& filename) { std::string files::read_string(const fs::path& filename) {
size_t size; size_t size;
std::unique_ptr<ubyte[]> bytes(read_bytes(filename, size)); auto bytes = read_bytes(filename, size);
if (bytes == nullptr) {
throw std::runtime_error(
"could not to load file '" + filename.string() + "'"
);
}
return std::string((const char*)bytes.get(), size); return std::string((const char*)bytes.get(), size);
} }
bool files::write_string(const fs::path& filename, const std::string content) { bool files::write_string(const fs::path& filename, std::string_view content) {
std::ofstream file(filename); std::ofstream file(filename);
if (!file) { if (!file) {
return false; return false;

View File

@ -38,7 +38,7 @@ namespace files {
uint append_bytes(const fs::path& file, const ubyte* data, size_t size); uint append_bytes(const fs::path& file, const ubyte* data, size_t size);
/// @brief Write string to the file /// @brief Write string to the file
bool write_string(const fs::path& filename, const std::string content); bool write_string(const fs::path& filename, std::string_view content);
/// @brief Write dynamic data to the JSON file /// @brief Write dynamic data to the JSON file
/// @param nice if true, human readable format will be used, otherwise /// @param nice if true, human readable format will be used, otherwise

View File

@ -51,6 +51,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
builder.add("samples", &settings.display.samples); builder.add("samples", &settings.display.samples);
builder.add("framerate", &settings.display.framerate); builder.add("framerate", &settings.display.framerate);
builder.add("fullscreen", &settings.display.fullscreen); builder.add("fullscreen", &settings.display.fullscreen);
builder.add("limit-fps-iconified", &settings.display.limitFpsIconified);
builder.section("camera"); builder.section("camera");
builder.add("sensitivity", &settings.camera.sensitivity); builder.add("sensitivity", &settings.camera.sensitivity);
@ -102,6 +103,26 @@ dv::value SettingsHandler::getValue(const std::string& name) const {
} }
} }
dv::value SettingsHandler::getDefault(const std::string& name) const {
auto found = map.find(name);
if (found == map.end()) {
throw std::runtime_error("setting '" + name + "' does not exist");
}
auto setting = found->second;
if (auto number = dynamic_cast<NumberSetting*>(setting)) {
return static_cast<number_t>(number->getDefault());
} else if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
return static_cast<integer_t>(integer->getDefault());
} else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
return flag->getDefault();
} else if (auto string = dynamic_cast<StringSetting*>(setting)) {
return string->getDefault();
} else {
throw std::runtime_error("type is not implemented for '" + name + "'");
}
}
std::string SettingsHandler::toString(const std::string& name) const { std::string SettingsHandler::toString(const std::string& name) const {
auto found = map.find(name); auto found = map.find(name);
if (found == map.end()) { if (found == map.end()) {

View File

@ -22,6 +22,7 @@ public:
SettingsHandler(EngineSettings& settings); SettingsHandler(EngineSettings& settings);
dv::value getValue(const std::string& name) const; dv::value getValue(const std::string& name) const;
dv::value getDefault(const std::string& name) const;
void setValue(const std::string& name, const dv::value& value); void setValue(const std::string& name, const dv::value& value);
std::string toString(const std::string& name) const; std::string toString(const std::string& name) const;
Setting* getSetting(const std::string& name) const; Setting* getSetting(const std::string& name) const;

View File

@ -37,10 +37,10 @@ LevelFrontend::LevelFrontend(
auto soundsCamera = currentPlayer->currentCamera.get(); auto soundsCamera = currentPlayer->currentCamera.get();
if (soundsCamera == currentPlayer->spCamera.get() || if (soundsCamera == currentPlayer->spCamera.get() ||
soundsCamera == currentPlayer->tpCamera.get()) { soundsCamera == currentPlayer->tpCamera.get()) {
soundsCamera = currentPlayer->camera.get(); soundsCamera = currentPlayer->fpCamera.get();
} }
bool relative = player == currentPlayer && bool relative = player == currentPlayer &&
soundsCamera == currentPlayer->camera.get(); soundsCamera == currentPlayer->fpCamera.get();
if (!relative) { if (!relative) {
pos = player->getPosition(); pos = player->getPosition();
} }

View File

@ -224,7 +224,7 @@ void Hud::processInput(bool visible) {
setPause(true); setPause(true);
} }
} }
if (!pause && Events::active(BIND_DEVTOOLS_CONSOLE)) { if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
showOverlay(assets->get<UiDocument>("core:console"), false); showOverlay(assets->get<UiDocument>("core:console"), false);
} }
if (!Window::isFocused() && !pause && !isInventoryOpen()) { if (!Window::isFocused() && !pause && !isInventoryOpen()) {

View File

@ -37,7 +37,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
auto menu = engine->getGUI()->getMenu(); auto menu = engine->getGUI()->getMenu();
menu->reset(); menu->reset();
controller = std::make_unique<LevelController>(settings, std::move(level)); controller = std::make_unique<LevelController>(engine, std::move(level));
frontend = std::make_unique<LevelFrontend>(controller->getPlayer(), controller.get(), assets); frontend = std::make_unique<LevelFrontend>(controller->getPlayer(), controller.get(), assets);
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer()); worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer());
@ -48,7 +48,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
worldRenderer->clear(); worldRenderer->clear();
})); }));
keepAlive(settings.camera.fov.observe([=](double value) { keepAlive(settings.camera.fov.observe([=](double value) {
controller->getPlayer()->camera->setFov(glm::radians(value)); controller->getPlayer()->fpCamera->setFov(glm::radians(value));
})); }));
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){ keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
controller->getLevel()->chunks->saveAndClear(); controller->getLevel()->chunks->saveAndClear();
@ -93,7 +93,7 @@ void LevelScreen::saveWorldPreview() {
int previewSize = settings.ui.worldPreviewSize.get(); int previewSize = settings.ui.worldPreviewSize.get();
// camera special copy for world preview // camera special copy for world preview
Camera camera = *player->camera; Camera camera = *player->fpCamera;
camera.setFov(glm::radians(70.0f)); camera.setFov(glm::radians(70.0f));
DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get()); DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get());
@ -101,7 +101,7 @@ void LevelScreen::saveWorldPreview() {
Viewport viewport(previewSize * 1.5, previewSize); Viewport viewport(previewSize * 1.5, previewSize);
DrawContext ctx(&pctx, viewport, batch.get()); DrawContext ctx(&pctx, viewport, batch.get());
worldRenderer->draw(ctx, &camera, false, true, 0.0f, postProcessing.get()); worldRenderer->draw(ctx, camera, false, true, 0.0f, postProcessing.get());
auto image = postProcessing->toImage(); auto image = postProcessing->toImage();
image->flipY(); image->flipY();
imageio::write(paths->resolve("world:preview.png").u8string(), image.get()); imageio::write(paths->resolve("world:preview.png").u8string(), image.get());
@ -164,7 +164,9 @@ void LevelScreen::draw(float delta) {
Viewport viewport(Window::width, Window::height); Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, batch.get()); DrawContext ctx(nullptr, viewport, batch.get());
worldRenderer->draw(ctx, camera.get(), hudVisible, hud->isPause(), delta, postProcessing.get()); worldRenderer->draw(
ctx, *camera, hudVisible, hud->isPause(), delta, postProcessing.get()
);
if (hudVisible) { if (hudVisible) {
hud->draw(ctx); hud->draw(ctx);

View File

@ -75,7 +75,7 @@ void Batch2D::vertex(
buffer[index++] = a; buffer[index++] = a;
} }
void Batch2D::texture(Texture* new_texture){ void Batch2D::texture(const Texture* new_texture){
if (currentTexture == new_texture) { if (currentTexture == new_texture) {
return; return;
} }

View File

@ -17,7 +17,7 @@ class Batch2D : public Flushable {
std::unique_ptr<Texture> blank; std::unique_ptr<Texture> blank;
size_t index; size_t index;
glm::vec4 color; glm::vec4 color;
Texture* currentTexture; const Texture* currentTexture;
DrawPrimitive primitive = DrawPrimitive::triangle; DrawPrimitive primitive = DrawPrimitive::triangle;
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f}; UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
@ -40,7 +40,7 @@ public:
~Batch2D(); ~Batch2D();
void begin(); void begin();
void texture(Texture* texture); void texture(const Texture* texture);
void untexture(); void untexture();
void setRegion(UVRegion region); void setRegion(UVRegion region);
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint); void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);

View File

@ -106,7 +106,7 @@ void Batch3D::face(
tint.r, tint.g, tint.b, tint.a); tint.r, tint.g, tint.b, tint.a);
} }
void Batch3D::texture(Texture* new_texture){ void Batch3D::texture(const Texture* new_texture){
if (currentTexture == new_texture) if (currentTexture == new_texture)
return; return;
flush(); flush();

View File

@ -18,7 +18,7 @@ class Batch3D : public Flushable {
std::unique_ptr<Texture> blank; std::unique_ptr<Texture> blank;
size_t index; size_t index;
Texture* currentTexture; const Texture* currentTexture;
void vertex( void vertex(
float x, float y, float z, float x, float y, float z,
@ -47,11 +47,36 @@ public:
~Batch3D(); ~Batch3D();
void begin(); void begin();
void texture(Texture* texture); void texture(const Texture* texture);
void sprite(glm::vec3 pos, glm::vec3 up, glm::vec3 right, float w, float h, const UVRegion& uv, glm::vec4 tint); void sprite(
void xSprite(float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading=true); glm::vec3 pos,
void cube(const glm::vec3 coords, const glm::vec3 size, const UVRegion(&texfaces)[6], const glm::vec4 tint, bool shading=true); glm::vec3 up,
void blockCube(const glm::vec3 size, const UVRegion(&texfaces)[6], const glm::vec4 tint, bool shading=true); glm::vec3 right,
float w,
float h,
const UVRegion& uv,
glm::vec4 tint
);
void xSprite(
float w,
float h,
const UVRegion& uv,
const glm::vec4 tint,
bool shading = true
);
void cube(
const glm::vec3 coords,
const glm::vec3 size,
const UVRegion (&texfaces)[6],
const glm::vec4 tint,
bool shading = true
);
void blockCube(
const glm::vec3 size,
const UVRegion (&texfaces)[6],
const glm::vec4 tint,
bool shading = true
);
void vertex(glm::vec3 pos, glm::vec2 uv, glm::vec4 tint); void vertex(glm::vec3 pos, glm::vec2 uv, glm::vec4 tint);
void point(glm::vec3 pos, glm::vec4 tint); void point(glm::vec3 pos, glm::vec4 tint);
void flush() override; void flush() override;

View File

@ -30,10 +30,10 @@ Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
} }
} }
void Cubemap::bind(){ void Cubemap::bind() const {
glBindTexture(GL_TEXTURE_CUBE_MAP, id); glBindTexture(GL_TEXTURE_CUBE_MAP, id);
} }
void Cubemap::unbind() { void Cubemap::unbind() const {
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
} }

View File

@ -7,6 +7,6 @@ class Cubemap : public GLTexture {
public: public:
Cubemap(uint width, uint height, ImageFormat format); Cubemap(uint width, uint height, ImageFormat format);
virtual void bind() override; virtual void bind() const override;
virtual void unbind() override; virtual void unbind() const override;
}; };

View File

@ -33,11 +33,11 @@ GLTexture::~GLTexture() {
glDeleteTextures(1, &id); glDeleteTextures(1, &id);
} }
void GLTexture::bind(){ void GLTexture::bind() const {
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
} }
void GLTexture::unbind() { void GLTexture::unbind() const {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }

View File

@ -10,8 +10,8 @@ public:
GLTexture(const ubyte* data, uint width, uint height, ImageFormat format); GLTexture(const ubyte* data, uint width, uint height, ImageFormat format);
virtual ~GLTexture(); virtual ~GLTexture();
virtual void bind() override; virtual void bind() const override;
virtual void unbind() override; virtual void unbind() const override;
virtual void reload(const ubyte* data); virtual void reload(const ubyte* data);
void setNearestFilter(); void setNearestFilter();

View File

@ -29,6 +29,11 @@ void Mesh::addBox(glm::vec3 pos, glm::vec3 size) {
addPlane(pos-X*size, Z*size, Y*size, -X); addPlane(pos-X*size, Z*size, Y*size, -X);
} }
void Mesh::scale(const glm::vec3& size) {
for (auto& vertex : vertices) {
vertex.coord *= size;
}
}
void Model::clean() { void Model::clean() {
meshes.erase( meshes.erase(

View File

@ -17,6 +17,7 @@ namespace model {
void addPlane(glm::vec3 pos, glm::vec3 right, glm::vec3 up, glm::vec3 norm); void addPlane(glm::vec3 pos, glm::vec3 right, glm::vec3 up, glm::vec3 norm);
void addBox(glm::vec3 pos, glm::vec3 size); void addBox(glm::vec3 pos, glm::vec3 size);
void scale(const glm::vec3& size);
}; };
struct Model { struct Model {

View File

@ -17,8 +17,8 @@ public:
virtual ~Texture() {} virtual ~Texture() {}
virtual void bind() = 0; virtual void bind() const = 0;
virtual void unbind() = 0; virtual void unbind() const = 0;
virtual void reload(const ImageData& image) = 0; virtual void reload(const ImageData& image) = 0;

View File

@ -1,5 +1,6 @@
#include "ModelBatch.hpp" #include "ModelBatch.hpp"
#include "assets/assets_util.hpp"
#include "graphics/core/Mesh.hpp" #include "graphics/core/Mesh.hpp"
#include "graphics/core/Model.hpp" #include "graphics/core/Model.hpp"
#include "graphics/core/Atlas.hpp" #include "graphics/core/Atlas.hpp"
@ -77,6 +78,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix,
const texture_names_map* varTextures, const texture_names_map* varTextures,
bool backlight) { bool backlight) {
glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
gpos += lightsOffset;
light_t light = chunks->getLight( light_t light = chunks->getLight(
std::floor(gpos.x), std::floor(gpos.x),
std::floor(std::min(CHUNK_H-1.0f, gpos.y)), std::floor(std::min(CHUNK_H-1.0f, gpos.y)),
@ -137,9 +139,13 @@ void ModelBatch::render() {
entries.clear(); entries.clear();
} }
void ModelBatch::setLightsOffset(const glm::vec3& offset) {
lightsOffset = offset;
}
void ModelBatch::setTexture(const std::string& name, void ModelBatch::setTexture(const std::string& name,
const texture_names_map* varTextures) { const texture_names_map* varTextures) {
if (name.at(0) == '$') { if (varTextures && name.at(0) == '$') {
const auto& found = varTextures->find(name); const auto& found = varTextures->find(name);
if (found == varTextures->end()) { if (found == varTextures->end()) {
return setTexture(nullptr); return setTexture(nullptr);
@ -147,25 +153,13 @@ void ModelBatch::setTexture(const std::string& name,
return setTexture(found->second, varTextures); return setTexture(found->second, varTextures);
} }
} }
size_t sep = name.find(':');
if (sep == std::string::npos) { auto textureRegion = util::get_texture_region(*assets, name, "blocks:notfound");
setTexture(assets->get<Texture>(name)); setTexture(textureRegion.texture);
} else { region = textureRegion.region;
auto atlas = assets->get<Atlas>(name.substr(0, sep));
if (atlas == nullptr) {
setTexture(nullptr);
} else {
setTexture(atlas->getTexture());
if (auto reg = atlas->getIf(name.substr(sep+1))) {
region = *reg;
} else {
setTexture("blocks:notfound", varTextures);
}
}
}
} }
void ModelBatch::setTexture(Texture* texture) { void ModelBatch::setTexture(const Texture* texture) {
if (texture == nullptr) { if (texture == nullptr) {
texture = blank.get(); texture = blank.get();
} }

View File

@ -31,9 +31,10 @@ class ModelBatch {
Assets* assets; Assets* assets;
Chunks* chunks; Chunks* chunks;
Texture* texture = nullptr; const Texture* texture = nullptr;
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f}; UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
const EngineSettings* settings; const EngineSettings* settings;
glm::vec3 lightsOffset {};
static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f}; static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f};
@ -71,7 +72,7 @@ class ModelBatch {
bool backlight); bool backlight);
void setTexture(const std::string& name, void setTexture(const std::string& name,
const texture_names_map* varTextures); const texture_names_map* varTextures);
void setTexture(Texture* texture); void setTexture(const Texture* texture);
void flush(); void flush();
struct DrawEntry { struct DrawEntry {
@ -96,4 +97,6 @@ public:
const model::Model* model, const model::Model* model,
const texture_names_map* varTextures); const texture_names_map* varTextures);
void render(); void render();
void setLightsOffset(const glm::vec3& offset);
}; };

View File

@ -0,0 +1,74 @@
#include "ModelsGenerator.hpp"
#include "assets/Assets.hpp"
#include "items/ItemDef.hpp"
#include "voxels/Block.hpp"
#include "content/Content.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("models-generator");
static void configure_textures(
model::Model& model,
const Block& blockDef,
const Assets& assets
) {
for (auto& mesh : model.meshes) {
auto& texture = mesh.texture;
if (texture.empty() || texture.at(0) != '$') {
continue;
}
try {
int index = std::stoi(texture.substr(1));
texture = "blocks:"+blockDef.textureFaces.at(index);
} catch (const std::invalid_argument& err) {
} catch (const std::runtime_error& err) {
logger.error() << err.what();
}
}
}
static model::Model create_flat_model(
const std::string& texture, const Assets& assets
) {
auto model = assets.require<model::Model>("drop-item");
for (auto& mesh : model.meshes) {
if (mesh.texture == "$0") {
mesh.texture = texture;
}
}
return model;
}
model::Model ModelsGenerator::generate(
const ItemDef& def, const Content& content, const Assets& assets
) {
if (def.iconType == ItemIconType::BLOCK) {
auto model = assets.require<model::Model>("block");
const auto& blockDef = content.blocks.require(def.icon);
if (blockDef.model == BlockModel::xsprite) {
return create_flat_model(
"blocks:" + blockDef.textureFaces.at(0), assets
);
}
for (auto& mesh : model.meshes) {
switch (blockDef.model) {
case BlockModel::aabb: {
glm::vec3 size = blockDef.hitboxes.at(0).size();
float m = glm::max(size.x, glm::max(size.y, size.z));
m = glm::min(1.0f, m);
mesh.scale(size / m);
break;
} default:
break;
}
mesh.scale(glm::vec3(0.3f));
}
configure_textures(model, blockDef, assets);
return model;
} else if (def.iconType == ItemIconType::SPRITE) {
return create_flat_model(def.icon, assets);
} else {
return model::Model();
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "graphics/core/Model.hpp"
struct ItemDef;
class Assets;
class Content;
class ModelsGenerator {
public:
static model::Model generate(
const ItemDef& def, const Content& content, const Assets& assets
);
};

View File

@ -58,11 +58,13 @@ Skybox::Skybox(uint size, Shader* shader)
Skybox::~Skybox() = default; Skybox::~Skybox() = default;
void Skybox::drawBackground(Camera* camera, Assets* assets, int width, int height) { void Skybox::drawBackground(
auto backShader = assets->get<Shader>("background"); const Camera& camera, const Assets& assets, int width, int height
) {
auto backShader = assets.get<Shader>("background");
backShader->use(); backShader->use();
backShader->uniformMatrix("u_view", camera->getView(false)); backShader->uniformMatrix("u_view", camera.getView(false));
backShader->uniform1f("u_zoom", camera->zoom*camera->getFov()/(M_PI*0.5f)); backShader->uniform1f("u_zoom", camera.zoom*camera.getFov()/(M_PI*0.5f));
backShader->uniform1f("u_ar", float(width)/float(height)); backShader->uniform1f("u_ar", float(width)/float(height));
backShader->uniform1i("u_cubemap", 1); backShader->uniform1i("u_cubemap", 1);
bind(); bind();
@ -93,8 +95,8 @@ void Skybox::drawStars(float angle, float opacity) {
void Skybox::draw( void Skybox::draw(
const DrawContext& pctx, const DrawContext& pctx,
Camera* camera, const Camera& camera,
Assets* assets, const Assets& assets,
float daytime, float daytime,
float fog) float fog)
{ {
@ -107,9 +109,9 @@ void Skybox::draw(
DrawContext ctx = pctx.sub(); DrawContext ctx = pctx.sub();
ctx.setBlendMode(BlendMode::addition); ctx.setBlendMode(BlendMode::addition);
auto p_shader = assets->get<Shader>("ui3d"); auto p_shader = assets.get<Shader>("ui3d");
p_shader->use(); p_shader->use();
p_shader->uniformMatrix("u_projview", camera->getProjView(false)); p_shader->uniformMatrix("u_projview", camera.getProjView(false));
p_shader->uniformMatrix("u_apply", glm::mat4(1.0f)); p_shader->uniformMatrix("u_apply", glm::mat4(1.0f));
batch3d->begin(); batch3d->begin();
@ -117,7 +119,7 @@ void Skybox::draw(
float opacity = glm::pow(1.0f-fog, 7.0f); float opacity = glm::pow(1.0f-fog, 7.0f);
for (auto& sprite : sprites) { for (auto& sprite : sprites) {
batch3d->texture(assets->get<Texture>(sprite.texture)); batch3d->texture(assets.get<Texture>(sprite.texture));
float sangle = daytime * float(M_PI)*2.0 + sprite.phase; float sangle = daytime * float(M_PI)*2.0 + sprite.phase;
float distance = sprite.distance; float distance = sprite.distance;
@ -136,6 +138,7 @@ void Skybox::draw(
} }
void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) { void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) {
float dayTime = t;
DrawContext ctx = pctx.sub(); DrawContext ctx = pctx.sub();
ctx.setDepthMask(false); ctx.setDepthMask(false);
ctx.setDepthTest(false); ctx.setDepthTest(false);
@ -180,10 +183,12 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
}; };
t *= M_PI*2.0f; t *= M_PI*2.0f;
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
shader->uniform1i("u_quality", quality); shader->uniform1i("u_quality", quality);
shader->uniform1f("u_mie", mie); shader->uniform1f("u_mie", mie);
shader->uniform1f("u_fog", mie - 1.0f); shader->uniform1f("u_fog", mie - 1.0f);
shader->uniform3f("u_lightDir", glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f))); shader->uniform3f("u_lightDir", lightDir);
shader->uniform1f("u_dayTime", dayTime);
for (uint face = 0; face < 6; face++) { for (uint face = 0; face < 6; face++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0);
shader->uniform3f("u_xaxis", xaxs[face]); shader->uniform3f("u_xaxis", xaxs[face]);

Some files were not shown because too many files have changed in this diff Show More