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
- name: Run tests
run: ctest --test-dir build
run: ctest --output-on-failure --test-dir build
- name: Create DMG
run: |

4
.gitignore vendored
View File

@ -48,3 +48,7 @@ appimage-build/
/res/content/*
!/res/content/base
*.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")
endif()
if (WIN32)
target_link_libraries(${PROJECT_NAME} VoxelEngineSrc winmm)
endif()
target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS})
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/*)
- **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
### *placing-block*

View File

@ -21,6 +21,7 @@ Subsections:
- [player](scripting/builtins/libplayer.md)
- [quat](scripting/builtins/libquat.md)
- [time](scripting/builtins/libtime.md)
- [utf8](scripting/builtins/libutf8.md)
- [vec2, vec3, vec4](scripting/builtins/libvecn.md)
- [world](scripting/builtins/libworld.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
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).
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
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
```lua
-- Returns worlds information.
world.get_list() -> tables array {
-- world name
name: str,
@ -27,6 +28,9 @@ world.get_total_time() -> number
-- Returns world seed.
world.get_seed() -> int
-- Returns generator name.
world.get_generator() -> str
-- Proves that this is the current time during the day
-- from 0.333(8 am) to 0.833(8 pm).
world.is_day() -> boolean

View File

@ -136,3 +136,30 @@ function on_hud_close(playerid: int)
```
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.
```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
input.is_pressed(code: str) -> bool
```

View File

@ -72,15 +72,13 @@ Fragments used by the generator must present in the directory:
## Structures
A structure is a set of rules for inserting a fragment into the world by the generator. It currently has no properties, being created as empty objects in the `generators/generator_name.files/structures.json` file. Example:
```lua
{
"tree0": {},
"tree1": {},
"tree2": {},
"tower": {},
"coal_ore0": {}
}
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:
```toml
tree0 = {}
tree1 = {}
tree2 = {}
tower = {}
coal_ore0 = {}
```
Currently, the name of the structure must match the name of the fragment used.
@ -136,7 +134,7 @@ structures = [
- block - plant block
- structure-chance - probability of generating a small structure on a surface block.
- structures - structures randomly placed on the surface.
- name - name of the structure declared in `structures.json`.
- name - name of the structure declared in `structures.toml`.
- weight - weight directly affecting the chance of choosing a specific structure.
### Biome Parameters
@ -301,6 +299,7 @@ Changes the heightmap size.
Available interpolation modes:
- 'nearest' - no interpolation
- 'linear' - bilinear interpolation
- 'cubic' - bicubic interpolation
### heightmap:crop(...)
@ -352,7 +351,15 @@ generation.save_fragment(
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

View File

@ -99,6 +99,7 @@ Inner text - initially entered text
- `placeholder` - placeholder text (used if the text field is empty)
- `supplier` - text supplier (called every frame)
- `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.
- `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true")

View File

@ -99,4 +99,4 @@
| save-skeleton-pose | поза скелета сущности | false |
| save-skeleton-textures | динамически назначенные текстуры | false |
| 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/`)
- `block` - отображает предпросмотр блока. В icon указывается строковый id блока который нужно отображать. Пример `base:wood`
### Модель предмета - `model-name`
Имя модели предмета. Модель будет загружена автоматически.
Значение по-умолчанию - `packid:itemname.model`.
Если модель не указана, будет сгенерирована автоматическию
## Поведение
### Устанавливаемый блок - `placing-block`

View File

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

View File

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

View File

@ -8,7 +8,7 @@ item.name(itemid: int) -> str
item.index(name: str) -> int
-- Возвращает название предмета, отображаемое в интерфейсе.
item.caption(blockid: int) -> str
item.caption(itemid: int) -> str
-- Возвращает максимальный размер стопки для предмета.
item.stack_size(itemid: int) -> int
@ -18,6 +18,15 @@ item.defs_count() -> int
-- Возвращает имя иконки предмета для использования в свойстве 'src' элемента image
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`
Используйте для хранения данныхm общих для всех миров.
Используйте для хранения данных общих для всех миров.
```python
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_generator() -> str
-- Проверяет существование мира по имени.
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
input.is_pressed(code: str) -> bool
```

View File

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

View File

@ -100,6 +100,7 @@
- `placeholder` - текст подстановки (используется если текстовое поле пусто)
- `supplier` - поставщик текста (вызывается каждый кадр)
- `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода
- `sub-consumer` - lua функция-приемник вводимого текста. Вызывается во время ввода или удаления текста.
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
- `multiline` - разрешает отображение многострочного текста.
- `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.pick="mouse:middle"
player.drop="key:q"
player.fast_interaction="key:x"
hud.inventory="key:tab"

View File

@ -1,9 +1,10 @@
{
"texture": "water",
"overlay-texture": "blocks:water",
"draw-group": 3,
"light-passing": true,
"sky-light-passing": false,
"obstacle": false,
"selectable": false,
"replaceable": true
}
}

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 body = entity.rigidbody
local rig = entity.skeleton
@ -27,7 +25,7 @@ end
do -- setup visuals
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
body:set_size({scale[1] * DROP_SCALE, bodysize, scale[3] * DROP_SCALE})
mat4.mul(matrix, rotation, matrix)
@ -38,9 +36,7 @@ end
function on_grounded(force)
local matrix = mat4.idt()
mat4.rotate(matrix, {0, 1, 0}, math.random()*360, matrix)
if model == "aabb" then
mat4.rotate(matrix, {1, 0, 0}, 90, matrix)
end
mat4.rotate(matrix, {1, 0, 0}, 90, matrix)
mat4.scale(matrix, scale, matrix)
rig:set_matrix(0, matrix)
inair = false

View File

@ -1,5 +1,3 @@
local item_models = require "core:item_models"
local tsf = entity.transform
local body = entity.rigidbody
local rig = entity.skeleton
@ -9,12 +7,8 @@ local itemIndex = rig:index("item")
local function refresh_model(id)
itemid = id
if id == 0 then
rig:set_model(itemIndex, "")
else
local scale = item_models.setup(itemid, rig, itemIndex)
rig:set_matrix(itemIndex, mat4.scale(scale))
end
rig:set_model(itemIndex, item.model_name(itemid))
rig:set_matrix(itemIndex, mat4.rotate({0, 1, 0}, -80))
end
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"
></textbox>
</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'
consumer='submit'
margin='0'

View File

@ -1,6 +1,14 @@
history = session.get_entry("commands_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()
local pid = hud.get_player()
local x,y,z = player.get_pos(pid)
@ -27,7 +35,7 @@ function on_history_up()
end
function on_history_down()
if history_pointer == #history-1 then
if history_pointer >= #history-1 then
return
end
history_pointer = history_pointer + 1

View File

@ -3,6 +3,7 @@
<!-- content is generated in script -->
</panel>
<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'>
<label>@Creator</label>

View File

@ -10,6 +10,8 @@
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
<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>
</panel>
</container>

View File

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

View File

@ -5,7 +5,7 @@
consumer='change_sensitivity'>
</trackbar>
<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 id='bindings_panel' size='380,204' padding='2' interval='1' max-length='300' color='#0000004C'>
<!-- content is generated in script -->

View File

@ -59,4 +59,6 @@ function on_open()
create_checkbox("display.fullscreen", "Fullscreen")
create_checkbox("camera.shaking", "Camera Shaking")
create_checkbox("camera.inertia", "Camera Inertia")
create_checkbox("camera.fov-effects", "Camera FOV Effects")
create_checkbox("display.limit-fps-iconified", "Limit Background FPS")
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]
end
)
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",
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)
for ly = 0, h - 1 do
for lz = 0, d - 1 do
for lx = 0, w - 1 do
block.set(x + lx, y + ly, z + lz, id)
for y=y1,y2 do
for z=z1,z2 do
for x=x1,x2 do
block.set(x, y, z, id)
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"
end
)
@ -151,22 +171,24 @@ 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",
function(args, kwargs)
local x = args[1]
local y = args[2]
local z = args[3]
local x1 = args[1]
local y1 = args[2]
local z1 = args[3]
local w = args[4]
local h = args[5]
local d = args[6]
local x2 = args[4]
local y2 = args[5]
local z2 = args[6]
local name = args[7]
local crop = args[8]
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'
generation.save_fragment(fragment, filename, crop)
@ -187,3 +209,17 @@ console.add_command(
" has been saved as "..file.resolve(filename))
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
-- events
------------------------------------------------
------------------- Events ---------------------
------------------------------------------------
events = {
handlers = {}
}
function events.on(event, func)
-- why an array? length is always = 1
-- FIXME: temporary fixed
events.handlers[event] = {} -- events.handlers[event] or {}
if events.handlers[event] == nil then
events.handlers[event] = {}
end
table.insert(events.handlers[event], func)
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)
for name, handlers in pairs(events.handlers) do
if name:sub(1, #prefix) == prefix then
events.handlers[name] = nil
local actualname = name
if type(name) == 'table' then
actualname = name[1]
end
if actualname:sub(1, #prefix+1) == prefix..':' then
events.handlers[actualname] = nil
end
end
end
@ -34,11 +48,13 @@ function pack.unload(prefix)
end
function events.emit(event, ...)
result = nil
if events.handlers[event] then
for _, func in ipairs(events.handlers[event]) do
result = result or func(...)
end
local result = nil
local handlers = events.handlers[event]
if handlers == nil then
return nil
end
for _, func in ipairs(handlers) do
result = result or func(...)
end
return result
end

View File

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

View File

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

View File

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

View File

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

View File

@ -5,11 +5,13 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <stdexcept>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#include "util/stringutil.hpp"
#include "graphics/core/TextureAnimation.hpp"
class Assets;
@ -84,6 +86,15 @@ public:
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>
std::optional<const assets_map*> getMap() const {
const auto& mapIter = assets.find(typeid(T));

View File

@ -16,6 +16,7 @@
#include "objects/rigging.hpp"
#include "util/ThreadPool.hpp"
#include "voxels/Block.hpp"
#include "items/ItemDef.hpp"
#include "Assets.hpp"
#include "assetload_funcs.hpp"
@ -212,12 +213,6 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.tryAddSound(material.breakSound);
}
addLayouts(
0,
"core",
loader.getPaths()->getMainRoot() / fs::path("layouts"),
loader
);
for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();
auto& info = pack->getInfo();
@ -228,7 +223,11 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
for (auto& entry : content->getSkeletons()) {
auto& skeleton = *entry.second;
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()) {
loader.add(
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/json.hpp"
#include "coders/obj.hpp"
#include "coders/vec3.hpp"
#include "constants.hpp"
#include "debug/Logger.hpp"
#include "files/engine_paths.hpp"
@ -23,6 +24,7 @@
#include "graphics/core/Texture.hpp"
#include "graphics/core/TextureAnimation.hpp"
#include "objects/rigging.hpp"
#include "util/stringutil.hpp"
#include "Assets.hpp"
#include "AssetsLoader.hpp"
@ -39,18 +41,32 @@ static bool animation(
Atlas* dstAtlas
);
assetload::postfunc assetload::
texture(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr<AssetCfg>&) {
std::shared_ptr<ImageData> image(
imageio::read(paths->find(filename + ".png").u8string()).release()
);
return [name, image](auto assets) {
assets->store(Texture::from(image.get()), name);
};
assetload::postfunc assetload::texture(
AssetsLoader*,
const ResPaths* paths,
const std::string& filename,
const std::string& name,
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);
};
} catch (const std::runtime_error& err) {
logger.error() << actualFile << ": " << err.what();
return [](auto) {};
}
}
assetload::postfunc assetload::
shader(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr<AssetCfg>&) {
assetload::postfunc assetload::shader(
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 fragmentFile = paths->find(filename + ".glslf");
@ -181,8 +197,7 @@ assetload::postfunc assetload::sound(
if (!fs::exists(variantFile)) {
break;
}
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM)
);
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
}
auto sound = baseSound.release();
@ -191,21 +206,50 @@ assetload::postfunc assetload::sound(
};
}
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 + ".obj");
static void request_textures(AssetsLoader* loader, const model::Model& model) {
for (auto& mesh : model.meshes) {
if (mesh.texture.find('$') == std::string::npos) {
auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
loader->add(
AssetType::TEXTURE, filename, mesh.texture, nullptr
);
}
}
}
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) {
for (auto& mesh : model->meshes) {
if (mesh.texture.find('$') == std::string::npos) {
auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
loader->add(
AssetType::TEXTURE, filename, mesh.texture, nullptr
);
}
}
request_textures(loader, *model);
assets->store(std::unique_ptr<model::Model>(model), name);
};
} 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;
}
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() {
if (pos == size) {
throw std::runtime_error("buffer underflow");

View File

@ -52,6 +52,8 @@ public:
ByteReader(const ubyte* data);
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)
ubyte get();
/// @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);
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) {
case 'n': ss << '\n'; 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);
}
}
} else {
def.rt.hitboxes->emplace_back(AABB(glm::vec3(1.0f)));
}
blockDefsIndices.push_back(&def);

View File

@ -326,6 +326,7 @@ void ContentLoader::loadBlock(
root.at("ui-layout").get(def.uiLayout);
root.at("inventory-size").get(def.inventorySize);
root.at("tick-interval").get(def.tickInterval);
root.at("overlay-texture").get(def.overlayTexture);
if (root.has("fields")) {
def.dataStruct = std::make_unique<StructLayout>();
@ -418,17 +419,18 @@ void ContentLoader::loadItem(
std::string iconTypeStr = "";
root.at("icon-type").get(iconTypeStr);
if (iconTypeStr == "none") {
def.iconType = item_icon_type::none;
def.iconType = ItemIconType::NONE;
} else if (iconTypeStr == "block") {
def.iconType = item_icon_type::block;
def.iconType = ItemIconType::BLOCK;
} else if (iconTypeStr == "sprite") {
def.iconType = item_icon_type::sprite;
def.iconType = ItemIconType::SPRITE;
} else if (iconTypeStr.length()) {
logger.error() << name << ": unknown icon type" << iconTypeStr;
}
root.at("icon").get(def.icon);
root.at("placing-block").get(def.placingBlock);
root.at("script-name").get(def.scriptName);
root.at("model-name").get(def.modelName);
root.at("stack-size").get(def.stackSize);
// 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);
item.generated = true;
item.caption = def.caption;
item.iconType = item_icon_type::block;
item.iconType = ItemIconType::BLOCK;
item.icon = full;
item.placingBlock = full;

View File

@ -202,6 +202,16 @@ void ContentLoader::loadGenerator(
map.at("biome-parameters").get(def.biomeParameters);
map.at("biome-bpd").get(def.biomesBPD);
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("wide-structs-chunks-radius").get(def.wideStructsChunksRadius);
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);
item.iconType = item_icon_type::none;
item.iconType = ItemIconType::NONE;
}
auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml");
if (fs::is_regular_file(bindsFile)) {
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.breakable = false;
ItemDef& item = builder->items.create(CORE_OBSTACLE+".item");
item.iconType = item_icon_type::block;
item.iconType = ItemIconType::BLOCK;
item.icon = CORE_OBSTACLE;
item.placingBlock = CORE_OBSTACLE;
item.caption = block.caption;
@ -59,7 +59,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
block.hitboxes = {AABB()};
block.obstacle = false;
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.placingBlock = CORE_STRUCT_AIR;
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_BUILD = "player.build";
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";
class EnginePaths;

View File

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

View File

@ -20,6 +20,7 @@
#include "frontend/menu.hpp"
#include "frontend/screens/Screen.hpp"
#include "frontend/screens/MenuScreen.hpp"
#include "graphics/render/ModelsGenerator.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/ImageData.hpp"
@ -131,7 +132,7 @@ void Engine::loadControls() {
if (fs::is_regular_file(controls_file)) {
logger.info() << "loading controls";
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()) {
renderFrame(batch);
}
Window::setFramerate(Window::isIconified() ? 20 :
settings.display.framerate.get());
Window::setFramerate(
Window::isIconified() && settings.display.limitFpsIconified.get()
? 20
: settings.display.framerate.get()
);
processPostRunnables();
@ -280,6 +284,17 @@ void Engine::loadAssets() {
}
}
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) {
@ -287,12 +302,14 @@ static void load_configs(const fs::path& root) {
auto bindsFile = configFolder/fs::path("bindings.toml");
if (fs::is_regular_file(bindsFile)) {
Events::loadBindings(
bindsFile.u8string(), files::read_string(bindsFile)
bindsFile.u8string(), files::read_string(bindsFile), BindType::BIND
);
}
}
void Engine::loadContent() {
scripting::cleanup();
auto resdir = paths->getResourcesFolder();
std::vector<std::string> names;
@ -338,6 +355,7 @@ void Engine::loadContent() {
}
void Engine::resetContent() {
scripting::cleanup();
auto resdir = paths->getResourcesFolder();
std::vector<PathsRoot> resRoots;
{
@ -388,6 +406,9 @@ double Engine::getDelta() const {
}
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("ambient"));
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
) {
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);
length = input.tellg();
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) {
size_t size;
std::unique_ptr<ubyte[]> bytes(read_bytes(filename, size));
if (bytes == nullptr) {
throw std::runtime_error(
"could not to load file '" + filename.string() + "'"
);
}
auto bytes = read_bytes(filename, 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);
if (!file) {
return false;

View File

@ -38,7 +38,7 @@ namespace files {
uint append_bytes(const fs::path& file, const ubyte* data, size_t size);
/// @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
/// @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("framerate", &settings.display.framerate);
builder.add("fullscreen", &settings.display.fullscreen);
builder.add("limit-fps-iconified", &settings.display.limitFpsIconified);
builder.section("camera");
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 {
auto found = map.find(name);
if (found == map.end()) {

View File

@ -22,6 +22,7 @@ public:
SettingsHandler(EngineSettings& settings);
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);
std::string toString(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();
if (soundsCamera == currentPlayer->spCamera.get() ||
soundsCamera == currentPlayer->tpCamera.get()) {
soundsCamera = currentPlayer->camera.get();
soundsCamera = currentPlayer->fpCamera.get();
}
bool relative = player == currentPlayer &&
soundsCamera == currentPlayer->camera.get();
soundsCamera == currentPlayer->fpCamera.get();
if (!relative) {
pos = player->getPosition();
}

View File

@ -224,7 +224,7 @@ void Hud::processInput(bool visible) {
setPause(true);
}
}
if (!pause && Events::active(BIND_DEVTOOLS_CONSOLE)) {
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
showOverlay(assets->get<UiDocument>("core:console"), false);
}
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();
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);
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();
}));
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([=](){
controller->getLevel()->chunks->saveAndClear();
@ -93,7 +93,7 @@ void LevelScreen::saveWorldPreview() {
int previewSize = settings.ui.worldPreviewSize.get();
// camera special copy for world preview
Camera camera = *player->camera;
Camera camera = *player->fpCamera;
camera.setFov(glm::radians(70.0f));
DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get());
@ -101,7 +101,7 @@ void LevelScreen::saveWorldPreview() {
Viewport viewport(previewSize * 1.5, previewSize);
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();
image->flipY();
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);
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) {
hud->draw(ctx);

View File

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

View File

@ -17,7 +17,7 @@ class Batch2D : public Flushable {
std::unique_ptr<Texture> blank;
size_t index;
glm::vec4 color;
Texture* currentTexture;
const Texture* currentTexture;
DrawPrimitive primitive = DrawPrimitive::triangle;
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
@ -40,7 +40,7 @@ public:
~Batch2D();
void begin();
void texture(Texture* texture);
void texture(const Texture* texture);
void untexture();
void setRegion(UVRegion region);
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);
}
void Batch3D::texture(Texture* new_texture){
void Batch3D::texture(const Texture* new_texture){
if (currentTexture == new_texture)
return;
flush();

View File

@ -18,7 +18,7 @@ class Batch3D : public Flushable {
std::unique_ptr<Texture> blank;
size_t index;
Texture* currentTexture;
const Texture* currentTexture;
void vertex(
float x, float y, float z,
@ -47,11 +47,36 @@ public:
~Batch3D();
void begin();
void texture(Texture* texture);
void sprite(glm::vec3 pos, glm::vec3 up, 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 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 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 point(glm::vec3 pos, glm::vec4 tint);
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);
}
void Cubemap::unbind() {
void Cubemap::unbind() const {
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

View File

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

View File

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

View File

@ -10,8 +10,8 @@ public:
GLTexture(const ubyte* data, uint width, uint height, ImageFormat format);
virtual ~GLTexture();
virtual void bind() override;
virtual void unbind() override;
virtual void bind() const override;
virtual void unbind() const override;
virtual void reload(const ubyte* data);
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);
}
void Mesh::scale(const glm::vec3& size) {
for (auto& vertex : vertices) {
vertex.coord *= size;
}
}
void Model::clean() {
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 addBox(glm::vec3 pos, glm::vec3 size);
void scale(const glm::vec3& size);
};
struct Model {

View File

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

View File

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

View File

@ -31,9 +31,10 @@ class ModelBatch {
Assets* assets;
Chunks* chunks;
Texture* texture = nullptr;
const Texture* texture = nullptr;
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
const EngineSettings* settings;
glm::vec3 lightsOffset {};
static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f};
@ -71,7 +72,7 @@ class ModelBatch {
bool backlight);
void setTexture(const std::string& name,
const texture_names_map* varTextures);
void setTexture(Texture* texture);
void setTexture(const Texture* texture);
void flush();
struct DrawEntry {
@ -96,4 +97,6 @@ public:
const model::Model* model,
const texture_names_map* varTextures);
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;
void Skybox::drawBackground(Camera* camera, Assets* assets, int width, int height) {
auto backShader = assets->get<Shader>("background");
void Skybox::drawBackground(
const Camera& camera, const Assets& assets, int width, int height
) {
auto backShader = assets.get<Shader>("background");
backShader->use();
backShader->uniformMatrix("u_view", camera->getView(false));
backShader->uniform1f("u_zoom", camera->zoom*camera->getFov()/(M_PI*0.5f));
backShader->uniformMatrix("u_view", camera.getView(false));
backShader->uniform1f("u_zoom", camera.zoom*camera.getFov()/(M_PI*0.5f));
backShader->uniform1f("u_ar", float(width)/float(height));
backShader->uniform1i("u_cubemap", 1);
bind();
@ -93,8 +95,8 @@ void Skybox::drawStars(float angle, float opacity) {
void Skybox::draw(
const DrawContext& pctx,
Camera* camera,
Assets* assets,
const Camera& camera,
const Assets& assets,
float daytime,
float fog)
{
@ -107,9 +109,9 @@ void Skybox::draw(
DrawContext ctx = pctx.sub();
ctx.setBlendMode(BlendMode::addition);
auto p_shader = assets->get<Shader>("ui3d");
auto p_shader = assets.get<Shader>("ui3d");
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));
batch3d->begin();
@ -117,7 +119,7 @@ void Skybox::draw(
float opacity = glm::pow(1.0f-fog, 7.0f);
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 distance = sprite.distance;
@ -136,6 +138,7 @@ void Skybox::draw(
}
void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) {
float dayTime = t;
DrawContext ctx = pctx.sub();
ctx.setDepthMask(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;
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
shader->uniform1i("u_quality", quality);
shader->uniform1f("u_mie", mie);
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++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0);
shader->uniform3f("u_xaxis", xaxs[face]);

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