Merge pull request #330 from MihailRis/item-models

Add item models
This commit is contained in:
MihailRis 2024-11-01 12:34:05 +03:00 committed by GitHub
commit f7ac76064b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 541 additions and 557 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: |

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

@ -18,4 +18,10 @@ 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
```

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

@ -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,12 @@ 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
```

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

Binary file not shown.

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

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

@ -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

@ -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"
@ -240,6 +241,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

@ -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

@ -125,7 +125,6 @@ static model::Mesh load_mesh(
if (flags == FLAG_ZLIB) {
throw std::runtime_error("compression is not supported yet");
}
assert(flags == 0);
std::vector<VertexAttribute> attributes;
for (int i = 0; i < attributeCount; i++) {
attributes.push_back(load_attribute(reader));

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

@ -25,7 +25,7 @@ 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");
@ -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

@ -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"
@ -280,6 +281,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) {

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

@ -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]);

View File

@ -3,6 +3,7 @@
#include <memory>
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include "typedefs.hpp"
#include "maths/fastmaths.hpp"
@ -27,21 +28,24 @@ class Skybox {
Shader* shader;
bool ready = false;
FastRandom random;
glm::vec3 lightDir;
std::unique_ptr<Mesh> mesh;
std::unique_ptr<Batch3D> batch3d;
std::vector<skysprite> sprites;
void drawStars(float angle, float opacity);
void drawBackground(Camera* camera, Assets* assets, int width, int height);
void drawBackground(
const Camera& camera, const Assets& assets, int width, int height
);
public:
Skybox(uint size, Shader* shader);
~Skybox();
void draw(
const DrawContext& pctx,
Camera* camera,
Assets* assets,
const Camera& camera,
const Assets& assets,
float daytime,
float fog
);
@ -52,4 +56,8 @@ public:
bool isReady() const {
return ready;
}
const glm::vec3 getLightDir() const {
return lightDir;
}
};

View File

@ -9,6 +9,7 @@
#include <memory>
#include "assets/Assets.hpp"
#include "assets/assets_util.hpp"
#include "content/Content.hpp"
#include "engine.hpp"
#include "frontend/LevelFrontend.hpp"
@ -78,17 +79,17 @@ WorldRenderer::WorldRenderer(
WorldRenderer::~WorldRenderer() = default;
bool WorldRenderer::drawChunk(
size_t index, Camera* camera, Shader* shader, bool culling
size_t index, const Camera& camera, Shader* shader, bool culling
) {
auto chunk = level->chunks->getChunks()[index];
if (!chunk->flags.lighted) {
return false;
}
float distance = glm::distance(
camera->position,
camera.position,
glm::vec3(
(chunk->x + 0.5f) * CHUNK_W,
camera->position.y,
camera.position.y,
(chunk->z + 0.5f) * CHUNK_D
)
);
@ -113,7 +114,9 @@ bool WorldRenderer::drawChunk(
return true;
}
void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) {
void WorldRenderer::drawChunks(
Chunks* chunks, const Camera& camera, Shader* shader
) {
auto assets = engine->getAssets();
auto atlas = assets->get<Atlas>("blocks");
@ -127,8 +130,8 @@ void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) {
if (chunks->getChunks()[i] == nullptr) continue;
indices.emplace_back(i);
}
float px = camera->position.x / static_cast<float>(CHUNK_W) - 0.5f;
float pz = camera->position.z / static_cast<float>(CHUNK_D) - 0.5f;
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
std::sort(indices.begin(), indices.end(), [chunks, px, pz](auto i, auto j) {
const auto& chunksBuffer = chunks->getChunks();
const auto a = chunksBuffer[i].get();
@ -141,7 +144,7 @@ void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) {
});
bool culling = engine->getSettings().graphics.frustumCulling.get();
if (culling) {
frustumCulling->update(camera->getProjView());
frustumCulling->update(camera.getProjView());
}
chunks->visible = 0;
for (size_t i = 0; i < indices.size(); i++) {
@ -151,20 +154,21 @@ void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) {
void WorldRenderer::setupWorldShader(
Shader* shader,
Camera* camera,
const Camera& camera,
const EngineSettings& settings,
float fogFactor
) {
shader->use();
shader->uniformMatrix("u_model", glm::mat4(1.0f));
shader->uniformMatrix("u_proj", camera->getProjection());
shader->uniformMatrix("u_view", camera->getView());
shader->uniformMatrix("u_proj", camera.getProjection());
shader->uniformMatrix("u_view", camera.getView());
shader->uniform1f("u_timer", timer);
shader->uniform1f("u_gamma", settings.graphics.gamma.get());
shader->uniform1f("u_fogFactor", fogFactor);
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader->uniform1f("u_dayTime", level->getWorld()->getInfo().daytime);
shader->uniform3f("u_cameraPos", camera->position);
shader->uniform2f("u_lightDir", skybox->getLightDir());
shader->uniform3f("u_cameraPos", camera.position);
shader->uniform1i("u_cubemap", 1);
auto indices = level->content->getIndices();
@ -186,7 +190,7 @@ void WorldRenderer::setupWorldShader(
void WorldRenderer::renderLevel(
const DrawContext&,
Camera* camera,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause
@ -251,10 +255,10 @@ void WorldRenderer::renderBlockSelection() {
}
void WorldRenderer::renderLines(
Camera* camera, Shader* linesShader, const DrawContext& pctx
const Camera& camera, Shader* linesShader, const DrawContext& pctx
) {
linesShader->use();
linesShader->uniformMatrix("u_projview", camera->getProjView());
linesShader->uniformMatrix("u_projview", camera.getProjView());
if (player->selection.vox.id != BLOCK_VOID) {
renderBlockSelection();
}
@ -268,7 +272,7 @@ void WorldRenderer::renderLines(
}
void WorldRenderer::renderDebugLines(
const DrawContext& pctx, Camera* camera, Shader* linesShader
const DrawContext& pctx, const Camera& camera, Shader* linesShader
) {
DrawContext ctx = pctx.sub(lineBatch.get());
const auto& viewport = ctx.getViewport();
@ -280,8 +284,8 @@ void WorldRenderer::renderDebugLines(
linesShader->use();
if (showChunkBorders) {
linesShader->uniformMatrix("u_projview", camera->getProjView());
glm::vec3 coord = player->camera->position;
linesShader->uniformMatrix("u_projview", camera.getProjView());
glm::vec3 coord = player->fpCamera->position;
if (coord.x < 0) coord.x--;
if (coord.z < 0) coord.z--;
int cx = floordiv(static_cast<int>(coord.x), CHUNK_W);
@ -310,7 +314,7 @@ void WorldRenderer::renderDebugLines(
-length,
length
) * model *
glm::inverse(camera->rotation)
glm::inverse(camera.rotation)
);
ctx.setDepthTest(false);
@ -327,9 +331,70 @@ void WorldRenderer::renderDebugLines(
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
}
void WorldRenderer::renderHands(const Camera& camera, const Assets& assets) {
auto entityShader = assets.get<Shader>("entity");
auto indices = level->content->getIndices();
// get current chosen item
const auto& inventory = player->getInventory();
int slot = player->getChosenSlot();
const ItemStack& stack = inventory->getSlot(slot);
const auto& def = indices->items.require(stack.getItemId());
// prepare modified HUD camera
Camera hudcam = camera;
hudcam.far = 100.0f;
hudcam.setFov(1.2f);
hudcam.position = {};
// configure model matrix
const glm::vec3 itemOffset(0.08f, 0.035f, -0.1);
static glm::mat4 prevRotation(1.0f);
const float speed = 24.0f;
glm::mat4 matrix = glm::translate(glm::mat4(1.0f), itemOffset);
matrix = glm::scale(matrix, glm::vec3(0.1f));
glm::mat4 rotation = camera.rotation;
glm::quat rot0 = glm::quat_cast(prevRotation);
glm::quat rot1 = glm::quat_cast(rotation);
glm::quat finalRot =
glm::slerp(rot0, rot1, static_cast<float>(engine->getDelta() * speed));
rotation = glm::mat4_cast(finalRot);
matrix = rotation * matrix *
glm::rotate(
glm::mat4(1.0f), -glm::pi<float>() * 0.5f, glm::vec3(0, 1, 0)
);
prevRotation = rotation;
auto offset = -(camera.position - player->getPosition());
float angle = glm::radians(player->cam.x - 90);
float cos = glm::cos(angle);
float sin = glm::sin(angle);
float newX = offset.x * cos - offset.z * sin;
float newZ = offset.x * sin + offset.z * cos;
offset = glm::vec3(newX, offset.y, newZ);
matrix = matrix * glm::translate(glm::mat4(1.0f), offset);
// render
modelBatch->setLightsOffset(camera.position);
modelBatch->draw(
matrix,
glm::vec3(1.0f),
assets.get<model::Model>(def.modelName),
nullptr
);
Window::clearDepth();
setupWorldShader(entityShader, hudcam, engine->getSettings(), 0.0f);
skybox->bind();
modelBatch->render();
modelBatch->setLightsOffset(glm::vec3());
skybox->unbind();
}
void WorldRenderer::draw(
const DrawContext& pctx,
Camera* camera,
Camera& camera,
bool hudVisible,
bool pause,
float delta,
@ -338,15 +403,15 @@ void WorldRenderer::draw(
timer += delta * !pause;
auto world = level->getWorld();
const Viewport& vp = pctx.getViewport();
camera->aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
const auto& settings = engine->getSettings();
const auto& worldInfo = world->getInfo();
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
auto assets = engine->getAssets();
auto linesShader = assets->get<Shader>("lines");
const auto& assets = *engine->getAssets();
auto linesShader = assets.get<Shader>("lines");
// World render scope with diegetic HUD included
{
@ -367,22 +432,70 @@ void WorldRenderer::draw(
// Debug lines
if (hudVisible) {
renderLines(camera, linesShader, ctx);
if (player->currentCamera == player->fpCamera) {
renderHands(camera, assets);
}
}
}
if (hudVisible && player->debug) {
renderDebugLines(wctx, camera, linesShader);
}
renderBlockOverlay(wctx, assets);
}
// Rendering fullscreen quad with
auto screenShader = assets->get<Shader>("screen");
auto screenShader = assets.get<Shader>("screen");
screenShader->use();
screenShader->uniform1f("u_timer", timer);
screenShader->uniform1f("u_dayTime", worldInfo.daytime);
postProcessing->render(pctx, screenShader);
}
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& assets) {
int x = std::floor(player->currentCamera->position.x);
int y = std::floor(player->currentCamera->position.y);
int z = std::floor(player->currentCamera->position.z);
auto block = level->chunks->get(x, y, z);
if (block && block->id) {
const auto& def =
level->content->getIndices()->blocks.require(block->id);
if (def.overlayTexture.empty()) {
return;
}
auto textureRegion = util::get_texture_region(
assets, def.overlayTexture, "blocks:notfound"
);
DrawContext ctx = wctx.sub();
ctx.setDepthTest(false);
ctx.setCullFace(false);
auto& shader = assets.require<Shader>("ui3d");
shader.use();
batch3d->begin();
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
auto light = level->chunks->getLight(x, y, z);
float s = Lightmap::extract(light, 3) / 15.0f;
glm::vec4 tint(
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
glm::min(1.0f, Lightmap::extract(light, 1) / 15.0f + s),
glm::min(1.0f, Lightmap::extract(light, 2) / 15.0f + s),
1.0f
);
batch3d->texture(textureRegion.texture);
batch3d->sprite(
glm::vec3(),
glm::vec3(0, 1, 0),
glm::vec3(1, 0, 0),
2,
2,
textureRegion.region,
tint
);
batch3d->flush();
}
}
void WorldRenderer::drawBorders(
int sx, int sy, int sz, int ex, int ey, int ez
) {

View File

@ -23,6 +23,7 @@ class Skybox;
class PostProcessing;
class DrawContext;
class ModelBatch;
class Assets;
struct EngineSettings;
namespace model {
@ -41,16 +42,20 @@ class WorldRenderer {
std::unique_ptr<ModelBatch> modelBatch;
float timer = 0.0f;
bool drawChunk(size_t index, Camera* camera, Shader* shader, bool culling);
void drawChunks(Chunks* chunks, Camera* camera, Shader* shader);
bool drawChunk(size_t index, const Camera& camera, Shader* shader, bool culling);
void drawChunks(Chunks* chunks, const Camera& camera, Shader* shader);
/// @brief Render block selection lines
void renderBlockSelection();
void renderHands(const Camera& camera, const Assets& assets);
/// @brief Render lines (selection and debug)
/// @param camera active camera
/// @param linesShader shader used
void renderLines(Camera* camera, Shader* linesShader, const DrawContext& pctx);
void renderLines(
const Camera& camera, Shader* linesShader, const DrawContext& pctx
);
/// @brief Render all debug lines (chunks borders, coord system guides)
/// @param context graphics context
@ -58,13 +63,15 @@ class WorldRenderer {
/// @param linesShader shader used
void renderDebugLines(
const DrawContext& context,
Camera* camera,
const Camera& camera,
Shader* linesShader
);
void renderBlockOverlay(const DrawContext& context, const Assets& assets);
void setupWorldShader(
Shader* shader,
Camera* camera,
const Camera& camera,
const EngineSettings& settings,
float fogFactor
);
@ -77,7 +84,7 @@ public:
void draw(
const DrawContext& context,
Camera* camera,
Camera& camera,
bool hudVisible,
bool pause,
float delta,
@ -91,7 +98,7 @@ public:
/// @param settings engine settings
void renderLevel(
const DrawContext& context,
Camera* camera,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause

View File

@ -1,6 +1,7 @@
#include "InventoryView.hpp"
#include "assets/Assets.hpp"
#include "assets/assets_util.hpp"
#include "content/Content.hpp"
#include "frontend/LevelFrontend.hpp"
#include "frontend/locale.hpp"
@ -161,9 +162,9 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
auto& item = indices->items.require(stack.getItemId());
switch (item.iconType) {
case item_icon_type::none:
case ItemIconType::NONE:
break;
case item_icon_type::block: {
case ItemIconType::BLOCK: {
const Block& cblock = content->blocks.require(item.icon);
batch->texture(previews->getTexture());
@ -173,23 +174,14 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
0, 0, 0, region, false, true, tint);
break;
}
case item_icon_type::sprite: {
size_t index = item.icon.find(':');
std::string name = item.icon.substr(index+1);
UVRegion region(0.0f, 0.0, 1.0f, 1.0f);
if (index == std::string::npos) {
batch->texture(assets->get<Texture>(name));
} else {
std::string atlasname = item.icon.substr(0, index);
auto atlas = assets->get<Atlas>(atlasname);
if (atlas && atlas->has(name)) {
region = atlas->get(name);
batch->texture(atlas->getTexture());
}
}
case ItemIconType::SPRITE: {
auto textureRegion =
util::get_texture_region(*assets, item.icon, "blocks:notfound");
batch->texture(textureRegion.texture);
batch->rect(
pos.x, pos.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
0, 0, 0, textureRegion.region, false, true, tint);
break;
}
}

View File

@ -14,4 +14,5 @@ void ItemDef::cloneTo(ItemDef& dst) {
dst.icon = icon;
dst.placingBlock = placingBlock;
dst.scriptName = scriptName;
dst.modelName = modelName;
}

View File

@ -12,10 +12,10 @@ struct item_funcs_set {
bool on_block_break_by : 1;
};
enum class item_icon_type {
none, // invisible (core:empty) must not be rendered
sprite, // textured quad: icon is `atlas_name:texture_name`
block, // block preview: icon is string block id
enum class ItemIconType {
NONE, // invisible (core:empty) must not be rendered
SPRITE, // textured quad: icon is `atlas_name:texture_name`
BLOCK, // block preview: icon is string block id
};
struct ItemDef {
@ -29,12 +29,14 @@ struct ItemDef {
bool generated = false;
uint8_t emission[4] {0, 0, 0, 0};
item_icon_type iconType = item_icon_type::sprite;
ItemIconType iconType = ItemIconType::SPRITE;
std::string icon = "blocks:notfound";
std::string placingBlock = "core:air";
std::string scriptName = name.substr(name.find(':') + 1);
std::string modelName = name + ".model";
struct {
itemid_t id;
blockid_t placingBlock;

View File

@ -41,7 +41,7 @@ CameraControl::CameraControl(
const std::shared_ptr<Player>& player, const CameraSettings& settings
)
: player(player),
camera(player->camera),
camera(player->fpCamera),
settings(settings),
offset(0.0f, 0.7f, 0.0f) {
}
@ -353,7 +353,7 @@ static void pick_block(
voxel* PlayerController::updateSelection(float maxDistance) {
auto indices = level->content->getIndices();
auto chunks = level->chunks.get();
auto camera = player->camera.get();
auto camera = player->fpCamera.get();
auto& selection = player->selection;
glm::vec3 end;
@ -416,7 +416,7 @@ voxel* PlayerController::updateSelection(float maxDistance) {
void PlayerController::processRightClick(const Block& def, const Block& target) {
const auto& selection = player->selection;
auto chunks = level->chunks.get();
auto camera = player->camera.get();
auto camera = player->fpCamera.get();
blockstate state {};
state.rotation = determine_rotation(&def, selection.normal, camera->dir);

View File

@ -10,55 +10,71 @@ static const ItemDef* get_item_def(lua::State* L, int idx) {
return indices->items.get(id);
}
static int l_item_name(lua::State* L) {
static int l_name(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
return lua::pushstring(L, def->name);
}
return 0;
}
static int l_item_index(lua::State* L) {
static int l_index(lua::State* L) {
auto name = lua::require_string(L, 1);
return lua::pushinteger(L, content->items.require(name).rt.id);
}
static int l_item_stack_size(lua::State* L) {
static int l_stack_size(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
return lua::pushinteger(L, def->stackSize);
}
return 0;
}
static int l_item_defs_count(lua::State* L) {
static int l_defs_count(lua::State* L) {
return lua::pushinteger(L, indices->items.count());
}
static int l_item_get_icon(lua::State* L) {
static int l_get_icon(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
switch (def->iconType) {
case item_icon_type::none:
case ItemIconType::NONE:
return 0;
case item_icon_type::sprite:
case ItemIconType::SPRITE:
return lua::pushstring(L, def->icon);
case item_icon_type::block:
case ItemIconType::BLOCK:
return lua::pushstring(L, "block-previews:" + def->icon);
}
}
return 0;
}
static int l_item_caption(lua::State* L) {
static int l_caption(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
return lua::pushstring(L, def->caption);
}
return 0;
}
static int l_placing_block(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
return lua::pushinteger(L, def->rt.placingBlock);
}
return 0;
}
static int l_model_name(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
return lua::pushstring(L, def->modelName);
}
return 0;
}
const luaL_Reg itemlib[] = {
{"index", lua::wrap<l_item_index>},
{"name", lua::wrap<l_item_name>},
{"stack_size", lua::wrap<l_item_stack_size>},
{"defs_count", lua::wrap<l_item_defs_count>},
{"icon", lua::wrap<l_item_get_icon>},
{"caption", lua::wrap<l_item_caption>},
{"index", lua::wrap<l_index>},
{"name", lua::wrap<l_name>},
{"stack_size", lua::wrap<l_stack_size>},
{"defs_count", lua::wrap<l_defs_count>},
{"icon", lua::wrap<l_get_icon>},
{"caption", lua::wrap<l_caption>},
{"placing_block", lua::wrap<l_placing_block>},
{"model_name", lua::wrap<l_model_name>},
{NULL, NULL}};

View File

@ -85,7 +85,7 @@ static int l_set_rot(lua::State* L) {
static int l_get_dir(lua::State* L) {
if (auto player = get_player(L, 1)) {
return lua::pushvec3(L, player->camera->front);
return lua::pushvec3(L, player->fpCamera->front);
}
return 0;
}

View File

@ -40,11 +40,11 @@ Player::Player(
position(position),
inventory(std::move(inv)),
eid(eid),
camera(level->getCamera("core:first-person")),
fpCamera(level->getCamera("core:first-person")),
spCamera(level->getCamera("core:third-person-front")),
tpCamera(level->getCamera("core:third-person-back")),
currentCamera(camera) {
camera->setFov(glm::radians(90.0f));
currentCamera(fpCamera) {
fpCamera->setFov(glm::radians(90.0f));
spCamera->setFov(glm::radians(90.0f));
tpCamera->setFov(glm::radians(90.0f));
}
@ -93,16 +93,16 @@ void Player::updateInput(PlayerInput& input, float delta) {
glm::vec3 dir(0, 0, 0);
if (input.moveForward) {
dir += camera->dir;
dir += fpCamera->dir;
}
if (input.moveBack) {
dir -= camera->dir;
dir -= fpCamera->dir;
}
if (input.moveRight) {
dir += camera->right;
dir += fpCamera->right;
}
if (input.moveLeft) {
dir -= camera->right;
dir -= fpCamera->right;
}
if (glm::length(dir) > 0.0f) {
dir = glm::normalize(dir);
@ -166,7 +166,7 @@ void Player::postUpdate() {
auto& skeleton = entity->getSkeleton();
skeleton.visible = currentCamera != camera;
skeleton.visible = currentCamera != fpCamera;
auto body = skeleton.config->find("body");
auto head = skeleton.config->find("head");
@ -252,7 +252,7 @@ entityid_t Player::getSelectedEntity() const {
return selectedEid;
}
std::shared_ptr<Inventory> Player::getInventory() const {
const std::shared_ptr<Inventory>& Player::getInventory() const {
return inventory;
}
@ -289,7 +289,7 @@ void Player::deserialize(const dv::value& src) {
const auto& posarr = src["position"];
dv::get_vec(posarr, position);
camera->position = position;
fpCamera->position = position;
const auto& rotarr = src["rotation"];
dv::get_vec(rotarr, cam);

View File

@ -52,7 +52,7 @@ class Player : public Object, public Serializable {
entityid_t eid;
entityid_t selectedEid;
public:
std::shared_ptr<Camera> camera, spCamera, tpCamera;
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
std::shared_ptr<Camera> currentCamera;
bool debug = false;
glm::vec3 cam {};
@ -91,7 +91,7 @@ public:
entityid_t getSelectedEntity() const;
std::shared_ptr<Inventory> getInventory() const;
const std::shared_ptr<Inventory>& getInventory() const;
glm::vec3 getPosition() const {
return position;

View File

@ -141,6 +141,7 @@ void Block::cloneTo(Block& dst) {
dst.uiLayout = uiLayout;
dst.inventorySize = inventorySize;
dst.tickInterval = tickInterval;
dst.overlayTexture = overlayTexture;
}
static std::set<std::string, std::less<>> RESERVED_BLOCK_FIELDS {

View File

@ -4,6 +4,7 @@
#include <optional>
#include <string>
#include <vector>
#include <array>
#include "maths/UVRegion.hpp"
#include "maths/aabb.hpp"
@ -111,7 +112,7 @@ public:
std::string caption;
/// @brief Textures set applied to block sides
std::string textureFaces[6]; // -x,x, -y,y, -z,z
std::array<std::string, 6> textureFaces; // -x,x, -y,y, -z,z
std::vector<std::string> modelTextures = {};
std::vector<BoxModel> modelBoxes = {};
@ -184,6 +185,9 @@ public:
/// @brief Block will be used instead of this if generated on surface
std::string surfaceReplacement = name;
/// @brief Texture will be shown on screen if camera is inside of the block
std::string overlayTexture;
/// @brief Default block layout will be used by hud.open_block(...)
std::string uiLayout = name;

View File

@ -29,21 +29,21 @@ void Camera::rotate(float x, float y, float z) {
updateVectors();
}
glm::mat4 Camera::getProjection() {
glm::mat4 Camera::getProjection() const {
constexpr float epsilon = 1e-6f; // 0.000001
float aspect_ratio = this->aspect;
if (std::fabs(aspect_ratio) < epsilon) {
aspect_ratio = (float)Window::width / (float)Window::height;
}
if (perspective)
return glm::perspective(fov * zoom, aspect_ratio, 0.05f, 1500.0f);
return glm::perspective(fov * zoom, aspect_ratio, near, far);
else if (flipped)
return glm::ortho(0.0f, fov * aspect_ratio, fov, 0.0f);
else
return glm::ortho(0.0f, fov * aspect_ratio, 0.0f, fov);
}
glm::mat4 Camera::getView(bool pos) {
glm::mat4 Camera::getView(bool pos) const {
glm::vec3 camera_pos = this->position;
if (!pos) {
camera_pos = glm::vec3(0.0f);
@ -55,7 +55,7 @@ glm::mat4 Camera::getView(bool pos) {
}
}
glm::mat4 Camera::getProjView(bool pos) {
glm::mat4 Camera::getProjView(bool pos) const {
return getProjection() * getView(pos);
}

View File

@ -18,6 +18,8 @@ public:
bool perspective = true;
bool flipped = false;
float aspect = 0.0f;
float near = 0.05f;
float far = 1500.0f;
Camera() {
updateVectors();
@ -27,9 +29,9 @@ public:
void updateVectors();
void rotate(float x, float y, float z);
glm::mat4 getProjection();
glm::mat4 getView(bool position = true);
glm::mat4 getProjView(bool position = true);
glm::mat4 getProjection() const;
glm::mat4 getView(bool position = true) const;
glm::mat4 getProjView(bool position = true) const;
void setFov(float fov);
float getFov() const;

View File

@ -5,7 +5,7 @@
TEST(VEC3, Decode) {
auto file = std::filesystem::u8path(
"../res/content/base/models/demo.vec3"
"../res/models/block.vec3"
);
auto bytes = files::read_bytes_buffer(file);
auto model = vec3::load(file.u8string(), bytes);