Merge pull request #174 from MihailRis/block_materials

Block materials
This commit is contained in:
MihailRis 2024-03-10 10:10:07 +03:00 committed by GitHub
commit 3f0c710a47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
73 changed files with 750 additions and 305 deletions

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/wood",
"place-sound": "blocks/wood_place",
"break-sound": "blocks/glass_break"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/grass",
"place-sound": "steps/grass",
"break-sound": "steps/grass"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/grass",
"place-sound": "blocks/ground_place",
"break-sound": "blocks/ground_break"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/ground",
"place-sound": "blocks/ground_place",
"break-sound": "blocks/ground_break"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/sand",
"steps-sound": "blocks/ground_place",
"break-sound": "blocks/ground_break"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/stone",
"place-sound": "blocks/stone_place",
"break-sound": "blocks/stone_break"
}

View File

@ -0,0 +1,5 @@
{
"steps-sound": "steps/wood",
"place-sound": "blocks/wood_place",
"break-sound": "blocks/wood_break"
}

View File

@ -1,3 +1,4 @@
{
"texture": "dirt"
"texture": "dirt",
"material": "base:ground"
}

View File

@ -1,5 +1,6 @@
{
"texture": "flower",
"material": "base:grass",
"draw-group": 1,
"light-passing": true,
"obstacle": false,

View File

@ -1,5 +1,6 @@
{
"texture": "glass",
"material": "base:glass",
"draw-group": 2,
"light-passing": true,
"sky-light-passing": true

View File

@ -1,5 +1,6 @@
{
"texture": "grass",
"material": "base:grass",
"draw-group": 1,
"light-passing": true,
"obstacle": false,

View File

@ -1,4 +1,5 @@
{
"material": "base:grass_block",
"texture-faces": [
"grass_side",
"grass_side",

View File

@ -1,3 +1,4 @@
{
"texture": "leaves"
"texture": "leaves",
"material": "base:grass"
}

View File

@ -7,6 +7,7 @@
"pane",
"pane"
],
"material": "base:wood",
"model": "aabb",
"hitbox": [0.0, 0.0, 0.0, 1.0, 1.0, 0.2],
"light-passing": true,

View File

@ -1,3 +1,4 @@
{
"texture": "planks"
"texture": "planks",
"material": "base:wood"
}

View File

@ -1,3 +1,4 @@
{
"texture": "sand"
"texture": "sand",
"material": "base:sand"
}

View File

@ -1,4 +1,5 @@
{
"material": "base:wood",
"texture-faces": [
"wood",
"wood",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -61,6 +61,14 @@ void addLayouts(int env, const std::string& prefix, const fs::path& folder, Asse
}
}
void AssetsLoader::tryAddSound(std::string name) {
if (name.empty()) {
return;
}
fs::path file = SOUNDS_FOLDER"/"+name+".ogg";
add(ASSET_SOUND, file, name);
}
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal");
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui");
@ -81,6 +89,13 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/sun.png", "misc/sun");
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/crosshair.png", "gui/crosshair");
for (auto& entry : content->getBlockMaterials()) {
auto& material = entry.second;
loader.tryAddSound(material.stepsSound);
loader.tryAddSound(material.placeSound);
loader.tryAddSound(material.breakSound);
}
addLayouts(0, "core", loader.getPaths()->getMainRoot()/fs::path("layouts"), loader);
for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();

View File

@ -49,6 +49,8 @@ class AssetsLoader {
std::map<int, aloader_func> loaders;
std::queue<aloader_entry> entries;
const ResPaths* paths;
void tryAddSound(std::string name);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
void addLoader(int tag, aloader_func func);

View File

@ -38,6 +38,11 @@ ALStream::ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSour
ALStream::~ALStream() {
bindSpeaker(0);
source = nullptr;
while (!unusedBuffers.empty()) {
al->freeBuffer(unusedBuffers.front());
unusedBuffers.pop();
}
}
std::shared_ptr<PCMStream> ALStream::getSource() const {
@ -135,6 +140,11 @@ void ALStream::update(double delta) {
return;
}
ALSpeaker* alspeaker = dynamic_cast<ALSpeaker*>(speaker);
if (alspeaker->stopped) {
speaker = 0;
return;
}
uint alsource = alspeaker->source;
unqueueBuffers(alsource);
@ -194,6 +204,8 @@ ALSpeaker::~ALSpeaker() {
}
void ALSpeaker::update(const Channel* channel, float masterVolume) {
if (source == 0)
return;
float gain = this->volume * channel->getVolume()*masterVolume;
AL_CHECK(alSourcef(source, AL_GAIN, gain));
@ -265,7 +277,9 @@ void ALSpeaker::stop() {
AL_CHECK(alSourceUnqueueBuffers(source, 1, &buffer));
al->freeBuffer(buffer);
}
AL_CHECK(alSourcei(source, AL_BUFFER, 0));
al->freeSource(source);
source = 0;
}
}
@ -395,8 +409,9 @@ uint ALAudio::getFreeSource(){
}
ALuint id;
alGenSources(1, &id);
if (!AL_GET_ERROR())
if (!AL_GET_ERROR()) {
return 0;
}
allsources.push_back(id);
return id;
}

View File

@ -17,7 +17,8 @@
#define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__)
#define AL_GET_ERROR() AL::check_errors(__FILE__, __LINE__)
namespace AL {
namespace AL {
/// @return true if no errors
bool check_errors(const std::string& filename, const std::uint_fast32_t line);
/// @brief alGetSourcef wrapper
@ -27,6 +28,8 @@ namespace AL {
/// @return field value or default
inline float getSourcef(uint source, ALenum field, float def=0.0f) {
float value = def;
if (source == 0)
return def;
AL_CHECK(alGetSourcef(source, field, &value));
return value;
}
@ -38,6 +41,8 @@ namespace AL {
/// @return field value or default
inline glm::vec3 getSource3f(uint source, ALenum field, glm::vec3 def={}) {
glm::vec3 value = def;
if (source == 0)
return def;
AL_CHECK(alGetSource3f(source, field, &value.x, &value.y, &value.z));
return value;
}
@ -49,6 +54,8 @@ namespace AL {
/// @return field value or default
inline float getSourcei(uint source, ALenum field, int def=0) {
int value = def;
if (source == 0)
return def;
AL_CHECK(alGetSourcei(source, field, &value));
return value;
}

View File

@ -249,6 +249,15 @@ speakerid_t audio::play(
int priority,
int channel
) {
if (sound == nullptr) {
return 0;
}
if (!sound->variants.empty()) {
size_t index = rand() % (sound->variants.size() + 1);
if (index < sound->variants.size()) {
sound = sound->variants.at(index).get();
}
}
Speaker* speaker = sound->newInstance(priority, channel);
if (speaker == nullptr) {
remove_lower_priority_speaker(priority);
@ -359,6 +368,10 @@ size_t audio::count_speakers() {
return speakers.size();
}
size_t audio::count_streams() {
return streams.size();
}
void audio::update(double delta) {
backend->update(delta);

View File

@ -484,6 +484,9 @@ namespace audio {
/// @brief Get alive speakers number (including paused)
extern size_t count_speakers();
/// @brief Get playing streams number (including paused)
extern size_t count_streams();
/// @brief Update audio streams and sound instanced
/// @param delta time elapsed since the last update (seconds)
extern void update(double delta);

View File

@ -10,8 +10,7 @@
#include "ContentPack.h"
#include "../logic/scripting/scripting.h"
ContentBuilder::~ContentBuilder() {
}
ContentBuilder::~ContentBuilder() {}
void ContentBuilder::add(Block* def) {
checkIdentifier(def->name);
@ -29,6 +28,10 @@ void ContentBuilder::add(ContentPackRuntime* pack) {
packs.emplace(pack->getId(), pack);
}
void ContentBuilder::add(BlockMaterial material) {
blockMaterials.emplace(material.name, material);
}
Block& ContentBuilder::createBlock(std::string id) {
auto found = blockDefs.find(id);
if (found != blockDefs.end()) {
@ -110,7 +113,12 @@ Content* ContentBuilder::build() {
auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices);
auto content = std::make_unique<Content>(
indices, std::move(groups), blockDefs, itemDefs, std::move(packs)
indices,
std::move(groups),
blockDefs,
itemDefs,
std::move(packs),
std::move(blockMaterials)
);
// Now, it's time to resolve foreign keys
@ -127,25 +135,27 @@ Content* ContentBuilder::build() {
ContentIndices::ContentIndices(
std::vector<Block*> blockDefs,
std::vector<ItemDef*> itemDefs)
: blockDefs(blockDefs),
itemDefs(itemDefs) {
}
std::vector<ItemDef*> itemDefs
) : blockDefs(blockDefs),
itemDefs(itemDefs)
{}
Content::Content(ContentIndices* indices,
std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs)
: blockDefs(blockDefs),
itemDefs(itemDefs),
indices(indices),
packs(std::move(packs)),
drawGroups(std::move(drawGroups)) {
}
Content::Content(
ContentIndices* indices,
std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
std::unordered_map<std::string, BlockMaterial> blockMaterials
) : blockDefs(blockDefs),
itemDefs(itemDefs),
indices(indices),
packs(std::move(packs)),
blockMaterials(std::move(blockMaterials)),
drawGroups(std::move(drawGroups))
{}
Content::~Content() {
}
Content::~Content() {}
Block* Content::findBlock(std::string id) const {
auto found = blockDefs.find(id);
@ -179,6 +189,14 @@ ItemDef& Content::requireItem(std::string id) const {
return *found->second;
}
const BlockMaterial* Content::findBlockMaterial(std::string id) const {
auto found = blockMaterials.find(id);
if (found == blockMaterials.end()) {
return nullptr;
}
return &found->second;
}
const ContentPackRuntime* Content::getPackRuntime(std::string id) const {
auto found = packs.find(id);
if (found == packs.end()) {
@ -187,6 +205,10 @@ const ContentPackRuntime* Content::getPackRuntime(std::string id) const {
return found->second.get();
}
const std::unordered_map<std::string, BlockMaterial>& Content::getBlockMaterials() const {
return blockMaterials;
}
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const {
return packs;
}

View File

@ -8,10 +8,10 @@
#include <unordered_map>
#include <set>
#include "../typedefs.h"
#include "../voxels/Block.h"
using DrawGroups = std::set<ubyte>;
class Block;
class ItemDef;
class Content;
class ContentPackRuntime;
@ -48,6 +48,8 @@ class ContentBuilder {
std::unordered_map<std::string, ItemDef*> itemDefs;
std::vector<std::string> itemIds;
std::unordered_map<std::string, BlockMaterial> blockMaterials;
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
public:
~ContentBuilder();
@ -55,6 +57,7 @@ public:
void add(Block* def);
void add(ItemDef* def);
void add(ContentPackRuntime* pack);
void add(BlockMaterial material);
Block& createBlock(std::string id);
ItemDef& createItem(std::string id);
@ -65,13 +68,15 @@ public:
Content* build();
};
/* Runtime defs cache: indices */
/// @brief Runtime defs cache: indices
class ContentIndices {
std::vector<Block*> blockDefs;
std::vector<ItemDef*> itemDefs;
public:
ContentIndices(std::vector<Block*> blockDefs,
std::vector<ItemDef*> itemDefs);
ContentIndices(
std::vector<Block*> blockDefs,
std::vector<ItemDef*> itemDefs
);
inline Block* getBlockDef(blockid_t id) const {
if (id >= blockDefs.size())
@ -109,14 +114,18 @@ class Content {
std::unordered_map<std::string, ItemDef*> itemDefs;
std::unique_ptr<ContentIndices> indices;
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
std::unordered_map<std::string, BlockMaterial> blockMaterials;
public:
std::unique_ptr<DrawGroups> const drawGroups;
Content(ContentIndices* indices,
std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs);
Content(
ContentIndices* indices,
std::unique_ptr<DrawGroups> drawGroups,
std::unordered_map<std::string, Block*> blockDefs,
std::unordered_map<std::string, ItemDef*> itemDefs,
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
std::unordered_map<std::string, BlockMaterial> blockMaterials
);
~Content();
inline ContentIndices* getIndices() const {
@ -129,8 +138,11 @@ public:
ItemDef* findItem(std::string id) const;
ItemDef& requireItem(std::string id) const;
const BlockMaterial* findBlockMaterial(std::string id) const;
const ContentPackRuntime* getPackRuntime(std::string id) const;
const std::unordered_map<std::string, BlockMaterial>& getBlockMaterials() const;
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const;
};

View File

@ -19,9 +19,9 @@ struct contententry {
// TODO: make it unified for all types of content
/* Content indices lookup table or report
used to convert world with different indices
Building with indices.json */
/// @brief Content indices lookup table or report
/// used to convert world with different indices
/// Building with indices.json
class ContentLUT {
std::vector<blockid_t> blocks;
std::vector<std::string> blockNames;

View File

@ -144,6 +144,8 @@ void ContentLoader::loadBlock(Block& def, std::string name, fs::path file) {
def.model = BlockModel::none;
}
root->str("material", def.material);
// rotation profile
std::string profile = "none";
root->str("rotation", profile);
@ -301,6 +303,15 @@ void ContentLoader::loadItem(ItemDef& def, std::string full, std::string name) {
}
}
BlockMaterial ContentLoader::loadBlockMaterial(fs::path file, std::string full) {
auto root = files::read_json(file);
BlockMaterial material {full};
root->str("steps-sound", material.stepsSound);
root->str("place-sound", material.placeSound);
root->str("break-sound", material.breakSound);
return material;
}
void ContentLoader::load(ContentBuilder& builder) {
std::cout << "-- loading pack [" << pack->id << "]" << std::endl;
@ -361,4 +372,13 @@ void ContentLoader::load(ContentBuilder& builder) {
stats.totalItems++;
}
}
fs::path materialsDir = folder / fs::u8path("block_materials");
if (fs::is_directory(materialsDir)) {
for (auto entry : fs::directory_iterator(materialsDir)) {
fs::path file = entry.path();
std::string name = pack->id+":"+file.stem().u8string();
builder.add(loadBlockMaterial(file, name));
}
}
}

View File

@ -1,12 +1,13 @@
#ifndef CONTENT_CONTENT_LOADER_H_
#define CONTENT_CONTENT_LOADER_H_
#include "../voxels/Block.h"
#include <string>
#include <filesystem>
namespace fs = std::filesystem;
class Block;
class ItemDef;
struct ContentPack;
class ContentBuilder;
@ -22,12 +23,15 @@ class ContentLoader {
void loadBlock(Block& def, std::string full, std::string name);
void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
void loadItem(ItemDef& def, std::string full, std::string name);
BlockMaterial loadBlockMaterial(fs::path file, std::string full);
public:
ContentLoader(ContentPack* pack);
bool fixPackIndices(std::filesystem::path folder,
dynamic::Map* indicesRoot,
std::string contentSection);
bool fixPackIndices(
fs::path folder,
dynamic::Map* indicesRoot,
std::string contentSection
);
void fixPackIndices();
void loadBlock(Block& def, std::string name, fs::path file);
void loadItem(ItemDef& def, std::string name, fs::path file);

View File

@ -1,16 +1,70 @@
#include "LevelFrontend.h"
#include "../world/Level.h"
#include "../assets/Assets.h"
#include "../graphics/Atlas.h"
#include "BlocksPreview.h"
#include "ContentGfxCache.h"
#include "../audio/audio.h"
#include "../world/Level.h"
#include "../voxels/Block.h"
#include "../assets/Assets.h"
#include "../graphics/Atlas.h"
#include "../content/Content.h"
#include "../logic/LevelController.h"
#include "../logic/PlayerController.h"
LevelFrontend::LevelFrontend(Level* level, Assets* assets)
: level(level),
assets(assets),
contentCache(std::make_unique<ContentGfxCache>(level->content, assets)),
blocksAtlas(BlocksPreview::build(contentCache.get(), assets, level->content)) {
: level(level),
assets(assets),
contentCache(std::make_unique<ContentGfxCache>(level->content, assets)),
blocksAtlas(BlocksPreview::build(contentCache.get(), assets, level->content))
{}
void LevelFrontend::observe(LevelController* controller) {
controller->getPlayerController()->listenBlockInteraction(
[=](Player*, glm::ivec3 pos, const Block* def, BlockInteraction type) {
auto material = level->content->findBlockMaterial(def->material);
if (material == nullptr) {
return;
}
if (type == BlockInteraction::step) {
auto sound = assets->getSound(material->stepsSound);
audio::play(
sound,
glm::vec3(),
true,
0.333f,
1.0f + (rand() % 6 - 3) * 0.05f,
false,
audio::PRIORITY_LOW,
audio::get_channel_index("regular")
);
} else {
audio::Sound* sound = nullptr;
switch (type) {
case BlockInteraction::placing:
sound = assets->getSound(material->placeSound);
break;
case BlockInteraction::destruction:
sound = assets->getSound(material->breakSound);
break;
case BlockInteraction::step:
break;
}
audio::play(
sound,
glm::vec3(pos.x, pos.y, pos.z) + 0.5f,
false,
1.0f,
1.0f + (rand() % 6 - 3) * 0.05f,
false,
audio::PRIORITY_NORMAL,
audio::get_channel_index("regular")
);
}
}
);
}
LevelFrontend::~LevelFrontend() {

View File

@ -8,6 +8,7 @@ class Level;
class Assets;
class BlocksPreview;
class ContentGfxCache;
class LevelController;
class LevelFrontend {
Level* level;
@ -18,6 +19,8 @@ public:
LevelFrontend(Level* level, Assets* assets);
~LevelFrontend();
void observe(LevelController* controller);
Level* getLevel() const;
Assets* getAssets() const;
ContentGfxCache* getContentGfxCache() const;

View File

@ -2,6 +2,7 @@
#include <memory>
#include "gui/controls.h"
#include "../audio/audio.h"
#include "../graphics/Mesh.h"
#include "../objects/Player.h"
#include "../physics/Hitbox.h"
@ -52,6 +53,10 @@ std::shared_ptr<UINode> create_debug_panel(
panel->add(create_label([](){
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
}));
panel->add(create_label([](){
return L"speakers: " + std::to_wstring(audio::count_speakers())+
L" streams: " + std::to_wstring(audio::count_streams());
}));
panel->add(create_label([=](){
auto& settings = engine->getSettings();
bool culling = settings.graphics.frustumCulling;

View File

@ -100,6 +100,8 @@ LevelScreen::LevelScreen(Engine* engine, Level* level) : Screen(engine) {
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer());
hud = std::make_unique<Hud>(engine, frontend.get(), controller->getPlayer());
frontend->observe(controller.get());
backlight = settings.graphics.backlight;
animator = std::make_unique<TextureAnimator>();
@ -156,9 +158,9 @@ void LevelScreen::update(float delta) {
auto player = controller->getPlayer();
auto camera = player->camera;
audio::set_listener(
camera->position,
camera->position-camera->dir,
player->hitbox->velocity,
camera->position+camera->dir,
camera->dir,
camera->up
);

View File

@ -57,3 +57,7 @@ Level* LevelController::getLevel() {
Player* LevelController::getPlayer() {
return player->getPlayer();
}
PlayerController* LevelController::getPlayerController() {
return player.get();
}

View File

@ -11,7 +11,7 @@
class Level;
class Player;
/* LevelController manages other controllers */
/// @brief LevelController manages other controllers
class LevelController {
EngineSettings& settings;
std::unique_ptr<Level> level;
@ -22,20 +22,22 @@ class LevelController {
public:
LevelController(EngineSettings& settings, Level* level);
/*
@param delta time elapsed since the last update
@param input is user input allowed to be handled
@param pause is world and player simulation paused
*/
void update(float delta,
bool input,
bool pause);
/// @param delta time elapsed since the last update
/// @param input is user input allowed to be handled
/// @param pause is world and player simulation paused
void update(
float delta,
bool input,
bool pause
);
void onWorldSave();
void onWorldQuit();
Level* getLevel();
Player* getPlayer();
PlayerController* getPlayerController();
};
#endif // LOGIC_LEVEL_CONTROLLER_H_

View File

@ -20,10 +20,14 @@
#include "../core_defs.h"
#define _USE_MATH_DEFINES
#include <cmath>
const float CAM_SHAKE_OFFSET = 0.025f;
const float CAM_SHAKE_OFFSET_Y = 0.031f;
const float CAM_SHAKE_SPEED = 1.75f;
const float CAM_SHAKE_DELTA_K = 10.0f;
const float STEPS_SPEED = 1.75f;
const float ZOOM_SPEED = 16.0f;
const float CROUCH_ZOOM = 0.9f;
const float RUN_ZOOM = 1.1f;
@ -32,11 +36,10 @@ const float CROUCH_SHIFT_Y = -0.2f;
CameraControl::CameraControl(std::shared_ptr<Player> player, const CameraSettings& settings)
: player(player),
camera(player->camera),
currentViewCamera(player->currentCamera),
settings(settings),
offset(0.0f, 0.7f, 0.0f) {
: player(player),
camera(player->camera),
settings(settings),
offset(0.0f, 0.7f, 0.0f) {
}
void CameraControl::refresh() {
@ -46,7 +49,10 @@ void CameraControl::refresh() {
void CameraControl::updateMouse(PlayerInput& input) {
glm::vec2& cam = player->cam;
float sensitivity = (input.zoom ? settings.sensitivity / 4.f : settings.sensitivity);
float sensitivity = (input.zoom
? settings.sensitivity / 4.f
: settings.sensitivity);
cam -= glm::degrees(Events::delta / (float)Window::height * sensitivity);
if (cam.y < -89.9f) {
@ -66,66 +72,93 @@ void CameraControl::updateMouse(PlayerInput& input) {
camera->rotate(glm::radians(cam.y), glm::radians(cam.x), 0);
}
void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) {
Hitbox* hitbox = player->hitbox.get();
glm::vec3 CameraControl::updateCameraShaking(float delta) {
glm::vec3 offset {};
auto hitbox = player->hitbox.get();
const float k = CAM_SHAKE_DELTA_K;
const float oh = CAM_SHAKE_OFFSET;
const float ov = CAM_SHAKE_OFFSET_Y;
const glm::vec3& vel = hitbox->velocity;
interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f;
if (hitbox->grounded && interpVel.y < 0.0f){
interpVel.y *= -30.0f;
}
shake = shake * (1.0f - delta * k);
if (hitbox->grounded) {
float f = glm::length(glm::vec2(vel.x, vel.z));
shakeTimer += delta * f * CAM_SHAKE_SPEED;
shake += f * delta * k;
}
offset += camera->right * glm::sin(shakeTimer) * oh * shake;
offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake;
offset -= glm::min(interpVel * 0.05f, 1.0f);
return offset;
}
void CameraControl::updateFovEffects(const PlayerInput& input, float delta) {
auto hitbox = player->hitbox.get();
bool crouch = input.shift && hitbox->grounded && !input.sprint;
float dt = fmin(1.0f, delta * ZOOM_SPEED);
float zoomValue = 1.0f;
if (crouch){
offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f);
zoomValue = CROUCH_ZOOM;
} else if (input.sprint){
zoomValue = RUN_ZOOM;
}
if (input.zoom)
zoomValue *= C_ZOOM;
camera->zoom = zoomValue * dt + camera->zoom * (1.0f - dt);
}
// temporary solution
// more extensible but uglier
void CameraControl::switchCamera() {
const std::vector<std::shared_ptr<Camera>> playerCameras {
camera, player->tpCamera, player->spCamera
};
auto index = std::distance(
playerCameras.begin(),
std::find_if(
playerCameras.begin(),
playerCameras.end(),
[=](auto ptr) {
return ptr.get() == player->currentCamera.get();
}
)
);
if (static_cast<size_t>(index) != playerCameras.size()) {
index = (index + 1) % playerCameras.size();
player->currentCamera = playerCameras.at(index);
}
}
void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) {
offset = glm::vec3(0.0f, 0.7f, 0.0f);
if (settings.shaking && !input.cheat) {
const float k = CAM_SHAKE_DELTA_K;
const float oh = CAM_SHAKE_OFFSET;
const float ov = CAM_SHAKE_OFFSET_Y;
const glm::vec3& vel = hitbox->velocity;
interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f;
if (hitbox->grounded && interpVel.y < 0.0f){
interpVel.y *= -30.0f;
}
shake = shake * (1.0f - delta * k);
if (hitbox->grounded) {
float f = glm::length(glm::vec2(vel.x, vel.z));
shakeTimer += delta * f * CAM_SHAKE_SPEED;
shake += f * delta * k;
}
offset += camera->right * glm::sin(shakeTimer) * oh * shake;
offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake;
offset -= glm::min(interpVel * 0.05f, 1.0f);
offset += updateCameraShaking(delta);
}
if (settings.fovEvents){
bool crouch = input.shift && hitbox->grounded && !input.sprint;
float dt = fmin(1.0f, delta * ZOOM_SPEED);
float zoomValue = 1.0f;
if (crouch){
offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f);
zoomValue = CROUCH_ZOOM;
} else if (input.sprint){
zoomValue = RUN_ZOOM;
}
if (input.zoom)
zoomValue *= C_ZOOM;
camera->zoom = zoomValue * dt + camera->zoom * (1.0f - dt);
updateFovEffects(input, delta);
}
if (input.cameraMode) {
switchCamera();
}
auto spCamera = player->spCamera;
auto tpCamera = player->tpCamera;
if (input.cameraMode) { //ugly but effective
if (player->currentCamera == camera)
player->currentCamera = tpCamera;
else if (player->currentCamera == spCamera)
player->currentCamera = camera;
else if (player->currentCamera == tpCamera)
player->currentCamera = spCamera;
}
if (player->currentCamera == spCamera) {
spCamera->position = chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) - 0.2f*(camera->front);
spCamera->position = chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) - 0.2f * camera->front;
spCamera->dir = -camera->dir;
spCamera->front = -camera->front;
}
else if (player->currentCamera == tpCamera) {
tpCamera->position = chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) + 0.2f * (camera->front);
tpCamera->position = chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) + 0.2f * camera->front;
tpCamera->dir = camera->dir;
tpCamera->front = camera->front;
}
@ -140,11 +173,62 @@ int PlayerController::selectedBlockStates = 0;
PlayerController::PlayerController(
Level* level,
const EngineSettings& settings,
BlocksController* blocksController)
: level(level),
player(level->getObject<Player>(0)),
camControl(player, settings.camera),
blocksController(blocksController) {
BlocksController* blocksController
) : level(level),
player(level->getObject<Player>(0)),
camControl(player, settings.camera),
blocksController(blocksController)
{}
void PlayerController::onBlockInteraction(
glm::ivec3 pos,
const Block* def,
BlockInteraction type
) {
for (auto callback : blockInteractionCallbacks) {
callback(player.get(), pos, def, type);
}
}
void PlayerController::onFootstep() {
auto hitbox = player->hitbox.get();
glm::vec3 pos = hitbox->position;
glm::vec3 half = hitbox->halfsize;
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
for (int offsetX = -1; offsetX <= 1; offsetX++) {
int x = std::floor(pos.x+half.x*offsetX);
int y = std::floor(pos.y-half.y*1.1f);
int z = std::floor(pos.z+half.z*offsetZ);
auto vox = level->chunks->get(x, y, z);
if (vox) {
auto def = level->content->getIndices()->getBlockDef(vox->id);
if (!def->obstacle)
continue;
onBlockInteraction(
glm::ivec3(x, y, z), def,
BlockInteraction::step
);
return;
}
}
}
}
void PlayerController::updateFootsteps(float delta) {
auto hitbox = player->hitbox.get();
if (hitbox->grounded) {
const glm::vec3& vel = hitbox->velocity;
float f = glm::length(glm::vec2(vel.x, vel.z));
stepsTimer += delta * f * STEPS_SPEED;
if (stepsTimer >= M_PI) {
stepsTimer = fmod(stepsTimer, M_PI);
onFootstep();
}
} else {
stepsTimer = M_PI;
}
}
void PlayerController::update(float delta, bool input, bool pause) {
@ -154,6 +238,7 @@ void PlayerController::update(float delta, bool input, bool pause) {
} else {
resetKeyboard();
}
updateFootsteps(delta);
updateCamera(delta, input);
updateControls(delta);
@ -289,12 +374,17 @@ void PlayerController::updateInteraction(){
uint8_t states = determine_rotation(def, norm, camera->dir);
if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) {
// TODO: move scripting to interaction callbacks
if (scripting::on_item_break_block(player.get(), item, x, y, z))
return;
}
Block* target = indices->getBlockDef(vox->id);
if (lclick && target->breakable){
onBlockInteraction(
glm::ivec3(x, y, z), target,
BlockInteraction::destruction
);
blocksController->breakBlock(player.get(), target, x, y, z);
}
if (rclick && !input.shift) {
@ -331,9 +421,14 @@ void PlayerController::updateInteraction(){
chosenBlock = 0;
}
if (chosenBlock != vox->id && chosenBlock) {
onBlockInteraction(
glm::ivec3(x, y, z), def,
BlockInteraction::placing
);
chunks->set(x, y, z, chosenBlock, states);
lighting->onBlockSet(x,y,z, chosenBlock);
if (def->rt.funcsset.onplaced) {
// TODO: move scripting to interaction callbacks
scripting::on_block_placed(player.get(), def, x, y, z);
}
blocksController->updateSides(x, y, z);
@ -358,3 +453,7 @@ void PlayerController::updateInteraction(){
Player* PlayerController::getPlayer() {
return player.get();
}
void PlayerController::listenBlockInteraction(on_block_interaction callback) {
blockInteractionCallbacks.push_back(callback);
}

View File

@ -2,6 +2,8 @@
#define PLAYER_CONTROL_H_
#include <memory>
#include <vector>
#include <functional>
#include <glm/glm.hpp>
#include "../settings.h"
@ -9,48 +11,87 @@
class Camera;
class Level;
class Block;
class BlocksController;
class CameraControl {
std::shared_ptr<Player> player;
std::shared_ptr<Camera> camera, currentViewCamera;
const CameraSettings& settings;
glm::vec3 offset;
float shake = 0.0f;
float shakeTimer = 0.0f;
glm::vec3 interpVel {0.0f};
std::shared_ptr<Player> player;
std::shared_ptr<Camera> camera;
const CameraSettings& settings;
glm::vec3 offset;
float shake = 0.0f;
float shakeTimer = 0.0f;
glm::vec3 interpVel {0.0f};
/// @brief Update shaking timer and calculate camera offset
/// @param delta delta time
/// @return camera offset
glm::vec3 updateCameraShaking(float delta);
/// @brief Update field-of-view effects
/// @param input player inputs
/// @param delta delta time
void updateFovEffects(const PlayerInput& input, float delta);
/// @brief Switch active player camera
void switchCamera();
public:
CameraControl(std::shared_ptr<Player> player, const CameraSettings& settings);
void updateMouse(PlayerInput& input);
void update(PlayerInput& input, float delta, Chunks* chunks);
void refresh();
CameraControl(std::shared_ptr<Player> player, const CameraSettings& settings);
void updateMouse(PlayerInput& input);
void update(PlayerInput& input, float delta, Chunks* chunks);
void refresh();
};
enum class BlockInteraction {
step,
destruction,
placing
};
using on_block_interaction = std::function<void(
Player*, glm::ivec3, const Block*, BlockInteraction type
)>;
class PlayerController {
Level* level;
std::shared_ptr<Player> player;
PlayerInput input;
CameraControl camControl;
Level* level;
std::shared_ptr<Player> player;
PlayerInput input;
CameraControl camControl;
BlocksController* blocksController;
void updateKeyboard();
void updateCamera(float delta, bool movement);
void resetKeyboard();
void updateControls(float delta);
void updateInteraction();
public:
static glm::vec3 selectedBlockPosition;
static glm::ivec3 selectedBlockNormal;
static glm::vec3 selectedPointPosition;
static int selectedBlockId;
static int selectedBlockStates;
std::vector<on_block_interaction> blockInteractionCallbacks;
PlayerController(Level* level,
const EngineSettings& settings,
BlocksController* blocksController);
void update(float delta, bool input, bool pause);
void updateKeyboard();
void updateCamera(float delta, bool movement);
void resetKeyboard();
void updateControls(float delta);
void updateInteraction();
void onBlockInteraction(
glm::ivec3 pos,
const Block* def,
BlockInteraction type
);
float stepsTimer = 0.0f;
void onFootstep();
void updateFootsteps(float delta);
public:
static glm::vec3 selectedBlockPosition;
static glm::ivec3 selectedBlockNormal;
static glm::vec3 selectedPointPosition;
static int selectedBlockId;
static int selectedBlockStates;
PlayerController(
Level* level,
const EngineSettings& settings,
BlocksController* blocksController
);
void update(float delta, bool input, bool pause);
Player* getPlayer();
void listenBlockInteraction(on_block_interaction callback);
};
#endif /* PLAYER_CONTROL_H_ */

View File

@ -19,14 +19,14 @@ const float CHEAT_SPEED_MUL = 5.0f;
const float JUMP_FORCE = 8.0f;
Player::Player(glm::vec3 position, float speed, std::shared_ptr<Inventory> inv) :
speed(speed),
chosenSlot(0),
inventory(inv),
camera(new Camera(position, glm::radians(90.0f))),
spCamera(new Camera(position, glm::radians(90.0f))),
tpCamera(new Camera(position, glm::radians(90.0f))),
currentCamera(camera),
hitbox(new Hitbox(position, glm::vec3(0.3f,0.9f,0.3f)))
speed(speed),
chosenSlot(0),
inventory(inv),
camera(std::make_shared<Camera>(position, glm::radians(90.0f))),
spCamera(std::make_shared<Camera>(position, glm::radians(90.0f))),
tpCamera(std::make_shared<Camera>(position, glm::radians(90.0f))),
currentCamera(camera),
hitbox(std::make_unique<Hitbox>(position, glm::vec3(0.3f,0.9f,0.3f)))
{
}
@ -34,116 +34,123 @@ Player::~Player() {
}
void Player::updateInput(
Level* level,
PlayerInput& input,
float delta) {
bool crouch = input.shift && hitbox->grounded && !input.sprint;
float speed = this->speed;
if (flight){
speed *= FLIGHT_SPEED_MUL;
}
if (input.cheat){
speed *= CHEAT_SPEED_MUL;
}
Level* level,
PlayerInput& input,
float delta) {
bool crouch = input.shift && hitbox->grounded && !input.sprint;
float speed = this->speed;
if (flight){
speed *= FLIGHT_SPEED_MUL;
}
if (input.cheat){
speed *= CHEAT_SPEED_MUL;
}
if (crouch) {
speed *= CROUCH_SPEED_MUL;
} else if (input.sprint) {
speed *= RUN_SPEED_MUL;
}
if (crouch) {
speed *= CROUCH_SPEED_MUL;
} else if (input.sprint) {
speed *= RUN_SPEED_MUL;
}
glm::vec3 dir(0,0,0);
if (input.moveForward){
dir.x += camera->dir.x;
dir.z += camera->dir.z;
}
if (input.moveBack){
dir.x -= camera->dir.x;
dir.z -= camera->dir.z;
}
if (input.moveRight){
dir.x += camera->right.x;
dir.z += camera->right.z;
}
if (input.moveLeft){
dir.x -= camera->right.x;
dir.z -= camera->right.z;
}
if (glm::length(dir) > 0.0f){
dir = glm::normalize(dir);
hitbox->velocity.x += dir.x * speed * delta * 9;
hitbox->velocity.z += dir.z * speed * delta * 9;
}
glm::vec3 dir(0,0,0);
if (input.moveForward){
dir.x += camera->dir.x;
dir.z += camera->dir.z;
}
if (input.moveBack){
dir.x -= camera->dir.x;
dir.z -= camera->dir.z;
}
if (input.moveRight){
dir.x += camera->right.x;
dir.z += camera->right.z;
}
if (input.moveLeft){
dir.x -= camera->right.x;
dir.z -= camera->right.z;
}
if (glm::length(dir) > 0.0f){
dir = glm::normalize(dir);
hitbox->velocity.x += dir.x * speed * delta * 9;
hitbox->velocity.z += dir.z * speed * delta * 9;
}
float vel = std::max(glm::length(hitbox->velocity * 0.25f), 1.0f);
int substeps = int(delta * vel * 1000);
substeps = std::min(100, std::max(1, substeps));
level->physics->step(level->chunks.get(), hitbox.get(),
delta, substeps,
crouch, flight ? 0.0f : 1.0f,
!noclip);
int substeps = int(delta * vel * 1000);
substeps = std::min(100, std::max(1, substeps));
level->physics->step(
level->chunks.get(),
hitbox.get(),
delta,
substeps,
crouch,
flight ? 0.0f : 1.0f,
!noclip
);
if (flight && hitbox->grounded) {
flight = false;
}
if (flight && hitbox->grounded) {
flight = false;
}
if (input.jump && hitbox->grounded){
hitbox->velocity.y = JUMP_FORCE;
}
if (input.jump && hitbox->grounded){
hitbox->velocity.y = JUMP_FORCE;
}
if ((input.flight && !noclip) ||
(input.noclip && flight == noclip)){
flight = !flight;
if (flight){
hitbox->grounded = false;
}
}
if (input.noclip) {
noclip = !noclip;
}
if ((input.flight && !noclip) ||
(input.noclip && flight == noclip)){
flight = !flight;
if (flight){
hitbox->grounded = false;
}
}
if (input.noclip) {
noclip = !noclip;
}
hitbox->linear_damping = PLAYER_GROUND_DAMPING;
if (flight){
hitbox->linear_damping = PLAYER_AIR_DAMPING;
hitbox->velocity.y *= 1.0f - delta * 9;
if (input.jump){
hitbox->velocity.y += speed * delta * 9;
}
if (input.shift){
hitbox->velocity.y -= speed * delta * 9;
}
}
if (!hitbox->grounded) {
hitbox->linear_damping = PLAYER_AIR_DAMPING;
}
hitbox->linear_damping = PLAYER_GROUND_DAMPING;
if (flight){
hitbox->linear_damping = PLAYER_AIR_DAMPING;
hitbox->velocity.y *= 1.0f - delta * 9;
if (input.jump){
hitbox->velocity.y += speed * delta * 9;
}
if (input.shift){
hitbox->velocity.y -= speed * delta * 9;
}
}
if (!hitbox->grounded) {
hitbox->linear_damping = PLAYER_AIR_DAMPING;
}
input.noclip = false;
input.flight = false;
input.noclip = false;
input.flight = false;
if (spawnpoint.y <= 0.1) {
attemptToFindSpawnpoint(level);
}
if (spawnpoint.y <= 0.1) {
attemptToFindSpawnpoint(level);
}
}
void Player::teleport(glm::vec3 position) {
hitbox->position = position;
hitbox->position = position;
}
void Player::attemptToFindSpawnpoint(Level* level) {
glm::vec3 ppos = hitbox->position;
glm::vec3 newpos {ppos.x + (rand() % 200 - 100),
rand() % 80 + 100,
ppos.z + (rand() % 200 - 100)};
while (newpos.y > 0 && !level->chunks->isObstacleBlock(newpos.x, newpos.y-2, newpos.z)) {
newpos.y--;
}
glm::vec3 ppos = hitbox->position;
glm::vec3 newpos (
ppos.x + (rand() % 200 - 100),
rand() % 80 + 100,
ppos.z + (rand() % 200 - 100)
);
while (newpos.y > 0 && !level->chunks->isObstacleBlock(newpos.x, newpos.y-2, newpos.z)) {
newpos.y--;
}
voxel* headvox = level->chunks->get(newpos.x, newpos.y+1, newpos.z);
if (level->chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) ||
headvox == nullptr || headvox->id != 0)
return;
spawnpoint = newpos + glm::vec3(0.5f, 0.0f, 0.5f);
teleport(spawnpoint);
voxel* headvox = level->chunks->get(newpos.x, newpos.y+1, newpos.z);
if (level->chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) ||
headvox == nullptr || headvox->id != 0)
return;
spawnpoint = newpos + glm::vec3(0.5f, 0.0f, 0.5f);
teleport(spawnpoint);
}
void Player::setChosenSlot(int index) {
@ -155,7 +162,7 @@ int Player::getChosenSlot() const {
}
float Player::getSpeed() const {
return speed;
return speed;
}
std::shared_ptr<Inventory> Player::getInventory() const {
@ -163,62 +170,62 @@ std::shared_ptr<Inventory> Player::getInventory() const {
}
void Player::setSpawnPoint(glm::vec3 spawnpoint) {
this->spawnpoint = spawnpoint;
this->spawnpoint = spawnpoint;
}
glm::vec3 Player::getSpawnPoint() const {
return spawnpoint;
return spawnpoint;
}
std::unique_ptr<dynamic::Map> Player::serialize() const {
glm::vec3 position = hitbox->position;
auto root = std::make_unique<dynamic::Map>();
auto& posarr = root->putList("position");
posarr.put(position.x);
posarr.put(position.y);
posarr.put(position.z);
glm::vec3 position = hitbox->position;
auto root = std::make_unique<dynamic::Map>();
auto& posarr = root->putList("position");
posarr.put(position.x);
posarr.put(position.y);
posarr.put(position.z);
auto& rotarr = root->putList("rotation");
rotarr.put(cam.x);
rotarr.put(cam.y);
auto& rotarr = root->putList("rotation");
rotarr.put(cam.x);
rotarr.put(cam.y);
auto& sparr = root->putList("spawnpoint");
sparr.put(spawnpoint.x);
sparr.put(spawnpoint.y);
sparr.put(spawnpoint.z);
auto& sparr = root->putList("spawnpoint");
sparr.put(spawnpoint.x);
sparr.put(spawnpoint.y);
sparr.put(spawnpoint.z);
root->put("flight", flight);
root->put("noclip", noclip);
root->put("flight", flight);
root->put("noclip", noclip);
root->put("chosen-slot", chosenSlot);
root->put("inventory", inventory->serialize().release());
return root;
}
void Player::deserialize(dynamic::Map *src) {
auto posarr = src->list("position");
glm::vec3& position = hitbox->position;
position.x = posarr->num(0);
position.y = posarr->num(1);
position.z = posarr->num(2);
camera->position = position;
auto posarr = src->list("position");
glm::vec3& position = hitbox->position;
position.x = posarr->num(0);
position.y = posarr->num(1);
position.z = posarr->num(2);
camera->position = position;
auto rotarr = src->list("rotation");
cam.x = rotarr->num(0);
cam.y = rotarr->num(1);
auto rotarr = src->list("rotation");
cam.x = rotarr->num(0);
cam.y = rotarr->num(1);
if (src->has("spawnpoint")) {
auto sparr = src->list("spawnpoint");
setSpawnPoint(glm::vec3(
sparr->num(0),
sparr->num(1),
sparr->num(2)
));
} else {
setSpawnPoint(position);
}
if (src->has("spawnpoint")) {
auto sparr = src->list("spawnpoint");
setSpawnPoint(glm::vec3(
sparr->num(0),
sparr->num(1),
sparr->num(2)
));
} else {
setSpawnPoint(position);
}
src->flag("flight", flight);
src->flag("noclip", noclip);
src->flag("flight", flight);
src->flag("noclip", noclip);
setChosenSlot(src->getInt("chosen-slot", getChosenSlot()));
auto invmap = src->map("inventory");

View File

@ -2,10 +2,8 @@
#include "../core_defs.h"
using glm::vec3;
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
: axisX(axisX), axisY(axisY), axisZ(axisZ)
: axisX(axisX), axisY(axisY), axisZ(axisZ)
{
fix = glm::ivec3(0);
if (isVectorHasNegatives(axisX)) fix -= axisX;
@ -14,9 +12,9 @@ CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
}
void CoordSystem::transform(AABB& aabb) const {
vec3 X(axisX);
vec3 Y(axisY);
vec3 Z(axisZ);
glm::vec3 X(axisX);
glm::vec3 Y(axisY);
glm::vec3 Z(axisZ);
aabb.a = X * aabb.a.x + Y * aabb.a.y + Z * aabb.a.z;
aabb.b = X * aabb.b.x + Y * aabb.b.y + Z * aabb.b.z;
aabb.a += fix;

View File

@ -18,8 +18,12 @@ inline constexpr uint FACE_PY = 3;
inline constexpr uint FACE_MZ = 4;
inline constexpr uint FACE_PZ = 5;
/// @brief Grid size used for physics solver collision checking with
/// complex hitboxes
inline constexpr uint BLOCK_AABB_GRID = 16;
inline std::string DEFAULT_MATERIAL = "base:stone";
struct block_funcs_set {
bool init: 1;
bool update: 1;
@ -35,7 +39,7 @@ struct CoordSystem {
glm::ivec3 axisY;
glm::ivec3 axisZ;
// Grid 3d position fix offset (for negative vectors)
/// @brief Grid 3d position fix offset (for negative vectors)
glm::ivec3 fix;
CoordSystem() = default;
@ -43,11 +47,8 @@ struct CoordSystem {
void transform(AABB& aabb) const;
static bool isVectorHasNegatives(glm::ivec3 vec) {
if (vec.x < 0 || vec.y < 0 || vec.z < 0) {
return true;
}
else return false;
inline bool isVectorHasNegatives(glm::ivec3 vec) {
return (vec.x < 0 || vec.y < 0 || vec.z < 0);
}
};
@ -56,56 +57,127 @@ struct BlockRotProfile {
std::string name;
CoordSystem variants[MAX_COUNT];
/* Wood logs, pillars, pipes */
/// @brief Wood logs, pillars, pipes
static const BlockRotProfile PIPE;
/* Doors, signs and other panes */
/// @brief Doors, signs and other panes
static const BlockRotProfile PANE;
};
enum class BlockModel {
none, // invisible
block, // default shape
xsprite, // X-shape (grass)
aabb, // box shaped as block hitbox
/// @brief invisible
none,
/// @brief default cube shape
block,
/// @brief X-shape (grass)
xsprite,
/// @brief box shape sized as block hitbox
aabb,
/// @brief custom model defined in json
custom
};
using BoxModel = AABB;
/// @brief Common kit of block properties applied to groups of blocks
struct BlockMaterial {
std::string name;
std::string stepsSound {""};
std::string placeSound {""};
std::string breakSound {""};
};
/// @brief Block properties definition
class Block {
public:
/// @brief Block string id (with prefix included)
std::string const name;
// 0 1 2 3 4 5
/// @brief Textures set applied to block sides
std::string textureFaces[6]; // -x,x, -y,y, -z,z
std::vector<std::string> modelTextures = {};
std::vector<BoxModel> modelBoxes = {};
std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons
std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there
/// @brief id of used BlockMaterial, may specify non-existing material
std::string material = DEFAULT_MATERIAL;
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive clouds)
uint8_t emission[4] {0, 0, 0, 0};
/// @brief Influences visible block sides for transparent blocks
uint8_t drawGroup = 0;
/// @brief Block model type
BlockModel model = BlockModel::block;
/// @brief Does the block passing lights into itself
bool lightPassing = false;
/// @brief Does the block passing top-down sky lights into itself
bool skyLightPassing = false;
/// @brief Is the block a physical obstacle
bool obstacle = true;
/// @brief Can the block be selected
bool selectable = true;
/// @brief Can the block be replaced with other.
/// Examples of replaceable blocks: air, flower, water
bool replaceable = false;
/// @brief Can player destroy the block
bool breakable = true;
/// @brief Can the block be oriented different ways
bool rotatable = false;
/// @brief Can the block exist without physical support be a solid block below
bool grounded = false;
/// @brief Turns off block item generation
bool hidden = false;
/// @brief Set of block physical hitboxes
std::vector<AABB> hitboxes;
/// @brief Set of available block rotations (coord-systems)
BlockRotProfile rotations;
/// @brief Item will be picked on MMB click on the block
std::string pickingItem = name+BLOCK_ITEM_SUFFIX;
std::string scriptName = name.substr(name.find(':')+1);
/// @brief Block script name in blocks/ without extension
std::string scriptName = name.substr(name.find(':')+1);
/// @brief Default block layout will be used by hud.open_block(...)
std::string uiLayout = name;
/// @brief Block inventory size. 0 - no inventory
uint inventorySize = 0;
/// @brief Runtime indices (content indexing results)
struct {
/// @brief block runtime integer id
blockid_t id;
/// @brief is the block completely opaque for render and raycast
bool solid = true;
/// @brief does the block emit any lights
bool emissive = false;
/// @brief set of hitboxes sets with all coord-systems precalculated
std::vector<AABB> hitboxes[BlockRotProfile::MAX_COUNT];
/// @brief set of block callbacks flags
block_funcs_set funcsset {};
/// @brief picking item integer id
itemid_t pickingItem = 0;
} rt;