diff --git a/res/content/base/block_materials/glass.json b/res/content/base/block_materials/glass.json new file mode 100644 index 00000000..86693a6e --- /dev/null +++ b/res/content/base/block_materials/glass.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/wood", + "place-sound": "blocks/wood_place", + "break-sound": "blocks/glass_break" +} diff --git a/res/content/base/block_materials/grass.json b/res/content/base/block_materials/grass.json new file mode 100644 index 00000000..148c80c0 --- /dev/null +++ b/res/content/base/block_materials/grass.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/grass", + "place-sound": "steps/grass", + "break-sound": "steps/grass" +} diff --git a/res/content/base/block_materials/grass_block.json b/res/content/base/block_materials/grass_block.json new file mode 100644 index 00000000..d866ff40 --- /dev/null +++ b/res/content/base/block_materials/grass_block.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/grass", + "place-sound": "blocks/ground_place", + "break-sound": "blocks/ground_break" +} diff --git a/res/content/base/block_materials/ground.json b/res/content/base/block_materials/ground.json new file mode 100644 index 00000000..36b15aed --- /dev/null +++ b/res/content/base/block_materials/ground.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/ground", + "place-sound": "blocks/ground_place", + "break-sound": "blocks/ground_break" +} diff --git a/res/content/base/block_materials/sand.json b/res/content/base/block_materials/sand.json new file mode 100644 index 00000000..00901142 --- /dev/null +++ b/res/content/base/block_materials/sand.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/sand", + "steps-sound": "blocks/ground_place", + "break-sound": "blocks/ground_break" +} diff --git a/res/content/base/block_materials/stone.json b/res/content/base/block_materials/stone.json new file mode 100644 index 00000000..a5b6df37 --- /dev/null +++ b/res/content/base/block_materials/stone.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/stone", + "place-sound": "blocks/stone_place", + "break-sound": "blocks/stone_break" +} diff --git a/res/content/base/block_materials/wood.json b/res/content/base/block_materials/wood.json new file mode 100644 index 00000000..d29b8a63 --- /dev/null +++ b/res/content/base/block_materials/wood.json @@ -0,0 +1,5 @@ +{ + "steps-sound": "steps/wood", + "place-sound": "blocks/wood_place", + "break-sound": "blocks/wood_break" +} diff --git a/res/content/base/blocks/dirt.json b/res/content/base/blocks/dirt.json index 023bf65f..1495377a 100644 --- a/res/content/base/blocks/dirt.json +++ b/res/content/base/blocks/dirt.json @@ -1,3 +1,4 @@ { - "texture": "dirt" + "texture": "dirt", + "material": "base:ground" } \ No newline at end of file diff --git a/res/content/base/blocks/flower.json b/res/content/base/blocks/flower.json index 7d2f9c73..db0609f8 100644 --- a/res/content/base/blocks/flower.json +++ b/res/content/base/blocks/flower.json @@ -1,5 +1,6 @@ { "texture": "flower", + "material": "base:grass", "draw-group": 1, "light-passing": true, "obstacle": false, diff --git a/res/content/base/blocks/glass.json b/res/content/base/blocks/glass.json index c56bdd55..902a2cf1 100644 --- a/res/content/base/blocks/glass.json +++ b/res/content/base/blocks/glass.json @@ -1,5 +1,6 @@ { "texture": "glass", + "material": "base:glass", "draw-group": 2, "light-passing": true, "sky-light-passing": true diff --git a/res/content/base/blocks/grass.json b/res/content/base/blocks/grass.json index 844d7baa..8d43b1a7 100644 --- a/res/content/base/blocks/grass.json +++ b/res/content/base/blocks/grass.json @@ -1,5 +1,6 @@ { "texture": "grass", + "material": "base:grass", "draw-group": 1, "light-passing": true, "obstacle": false, diff --git a/res/content/base/blocks/grass_block.json b/res/content/base/blocks/grass_block.json index 8c3cfb14..c0f6fa48 100644 --- a/res/content/base/blocks/grass_block.json +++ b/res/content/base/blocks/grass_block.json @@ -1,4 +1,5 @@ { + "material": "base:grass_block", "texture-faces": [ "grass_side", "grass_side", diff --git a/res/content/base/blocks/leaves.json b/res/content/base/blocks/leaves.json index ee0e38de..d8731480 100644 --- a/res/content/base/blocks/leaves.json +++ b/res/content/base/blocks/leaves.json @@ -1,3 +1,4 @@ { - "texture": "leaves" + "texture": "leaves", + "material": "base:grass" } \ No newline at end of file diff --git a/res/content/base/blocks/pane.json b/res/content/base/blocks/pane.json index f82f9e2d..0eebec49 100644 --- a/res/content/base/blocks/pane.json +++ b/res/content/base/blocks/pane.json @@ -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, diff --git a/res/content/base/blocks/planks.json b/res/content/base/blocks/planks.json index c3e31ff3..508fac23 100644 --- a/res/content/base/blocks/planks.json +++ b/res/content/base/blocks/planks.json @@ -1,3 +1,4 @@ { - "texture": "planks" + "texture": "planks", + "material": "base:wood" } \ No newline at end of file diff --git a/res/content/base/blocks/sand.json b/res/content/base/blocks/sand.json index 46b50319..08a7d4f4 100644 --- a/res/content/base/blocks/sand.json +++ b/res/content/base/blocks/sand.json @@ -1,3 +1,4 @@ { - "texture": "sand" + "texture": "sand", + "material": "base:sand" } \ No newline at end of file diff --git a/res/content/base/blocks/wood.json b/res/content/base/blocks/wood.json index e29d126a..db77a6bc 100644 --- a/res/content/base/blocks/wood.json +++ b/res/content/base/blocks/wood.json @@ -1,4 +1,5 @@ { + "material": "base:wood", "texture-faces": [ "wood", "wood", diff --git a/res/content/base/sounds/blocks/glass_break.ogg b/res/content/base/sounds/blocks/glass_break.ogg new file mode 100644 index 00000000..1da0b6a4 Binary files /dev/null and b/res/content/base/sounds/blocks/glass_break.ogg differ diff --git a/res/content/base/sounds/blocks/ground_break.ogg b/res/content/base/sounds/blocks/ground_break.ogg new file mode 100644 index 00000000..4a3c1c5e Binary files /dev/null and b/res/content/base/sounds/blocks/ground_break.ogg differ diff --git a/res/content/base/sounds/blocks/ground_place.ogg b/res/content/base/sounds/blocks/ground_place.ogg new file mode 100644 index 00000000..af8ab36b Binary files /dev/null and b/res/content/base/sounds/blocks/ground_place.ogg differ diff --git a/res/content/base/sounds/blocks/stone_break.ogg b/res/content/base/sounds/blocks/stone_break.ogg new file mode 100644 index 00000000..b54a78a8 Binary files /dev/null and b/res/content/base/sounds/blocks/stone_break.ogg differ diff --git a/res/content/base/sounds/blocks/stone_place.ogg b/res/content/base/sounds/blocks/stone_place.ogg new file mode 100644 index 00000000..12b0a27c Binary files /dev/null and b/res/content/base/sounds/blocks/stone_place.ogg differ diff --git a/res/content/base/sounds/blocks/wood_break.ogg b/res/content/base/sounds/blocks/wood_break.ogg new file mode 100644 index 00000000..d0fa78cc Binary files /dev/null and b/res/content/base/sounds/blocks/wood_break.ogg differ diff --git a/res/content/base/sounds/blocks/wood_place.ogg b/res/content/base/sounds/blocks/wood_place.ogg new file mode 100644 index 00000000..fea80562 Binary files /dev/null and b/res/content/base/sounds/blocks/wood_place.ogg differ diff --git a/res/content/base/sounds/steps/grass_0.ogg b/res/content/base/sounds/steps/grass_0.ogg new file mode 100644 index 00000000..07560d4b Binary files /dev/null and b/res/content/base/sounds/steps/grass_0.ogg differ diff --git a/res/content/base/sounds/steps/grass_1.ogg b/res/content/base/sounds/steps/grass_1.ogg new file mode 100644 index 00000000..ed78467e Binary files /dev/null and b/res/content/base/sounds/steps/grass_1.ogg differ diff --git a/res/content/base/sounds/steps/grass_2.ogg b/res/content/base/sounds/steps/grass_2.ogg new file mode 100644 index 00000000..969687a0 Binary files /dev/null and b/res/content/base/sounds/steps/grass_2.ogg differ diff --git a/res/content/base/sounds/steps/grass_3.ogg b/res/content/base/sounds/steps/grass_3.ogg new file mode 100644 index 00000000..645ebd53 Binary files /dev/null and b/res/content/base/sounds/steps/grass_3.ogg differ diff --git a/res/content/base/sounds/steps/ground_0.ogg b/res/content/base/sounds/steps/ground_0.ogg new file mode 100644 index 00000000..bb22163e Binary files /dev/null and b/res/content/base/sounds/steps/ground_0.ogg differ diff --git a/res/content/base/sounds/steps/ground_1.ogg b/res/content/base/sounds/steps/ground_1.ogg new file mode 100644 index 00000000..320ec378 Binary files /dev/null and b/res/content/base/sounds/steps/ground_1.ogg differ diff --git a/res/content/base/sounds/steps/ground_2.ogg b/res/content/base/sounds/steps/ground_2.ogg new file mode 100644 index 00000000..d9c59c73 Binary files /dev/null and b/res/content/base/sounds/steps/ground_2.ogg differ diff --git a/res/content/base/sounds/steps/ground_3.ogg b/res/content/base/sounds/steps/ground_3.ogg new file mode 100644 index 00000000..a9a62a09 Binary files /dev/null and b/res/content/base/sounds/steps/ground_3.ogg differ diff --git a/res/content/base/sounds/steps/sand_0.ogg b/res/content/base/sounds/steps/sand_0.ogg new file mode 100644 index 00000000..ada388c3 Binary files /dev/null and b/res/content/base/sounds/steps/sand_0.ogg differ diff --git a/res/content/base/sounds/steps/sand_1.ogg b/res/content/base/sounds/steps/sand_1.ogg new file mode 100644 index 00000000..caa96930 Binary files /dev/null and b/res/content/base/sounds/steps/sand_1.ogg differ diff --git a/res/content/base/sounds/steps/sand_2.ogg b/res/content/base/sounds/steps/sand_2.ogg new file mode 100644 index 00000000..ef6a5922 Binary files /dev/null and b/res/content/base/sounds/steps/sand_2.ogg differ diff --git a/res/content/base/sounds/steps/sand_3.ogg b/res/content/base/sounds/steps/sand_3.ogg new file mode 100644 index 00000000..dc309eb5 Binary files /dev/null and b/res/content/base/sounds/steps/sand_3.ogg differ diff --git a/res/content/base/sounds/steps/snow_0.ogg b/res/content/base/sounds/steps/snow_0.ogg new file mode 100644 index 00000000..e50bd5fc Binary files /dev/null and b/res/content/base/sounds/steps/snow_0.ogg differ diff --git a/res/content/base/sounds/steps/snow_1.ogg b/res/content/base/sounds/steps/snow_1.ogg new file mode 100644 index 00000000..c7409c15 Binary files /dev/null and b/res/content/base/sounds/steps/snow_1.ogg differ diff --git a/res/content/base/sounds/steps/snow_2.ogg b/res/content/base/sounds/steps/snow_2.ogg new file mode 100644 index 00000000..b4f44e98 Binary files /dev/null and b/res/content/base/sounds/steps/snow_2.ogg differ diff --git a/res/content/base/sounds/steps/snow_3.ogg b/res/content/base/sounds/steps/snow_3.ogg new file mode 100644 index 00000000..33f0161d Binary files /dev/null and b/res/content/base/sounds/steps/snow_3.ogg differ diff --git a/res/content/base/sounds/steps/snow_4.ogg b/res/content/base/sounds/steps/snow_4.ogg new file mode 100644 index 00000000..5ad40d6d Binary files /dev/null and b/res/content/base/sounds/steps/snow_4.ogg differ diff --git a/res/content/base/sounds/steps/stone_0.ogg b/res/content/base/sounds/steps/stone_0.ogg new file mode 100644 index 00000000..1ff4a84c Binary files /dev/null and b/res/content/base/sounds/steps/stone_0.ogg differ diff --git a/res/content/base/sounds/steps/stone_1.ogg b/res/content/base/sounds/steps/stone_1.ogg new file mode 100644 index 00000000..71b8b388 Binary files /dev/null and b/res/content/base/sounds/steps/stone_1.ogg differ diff --git a/res/content/base/sounds/steps/stone_2.ogg b/res/content/base/sounds/steps/stone_2.ogg new file mode 100644 index 00000000..6802e5f9 Binary files /dev/null and b/res/content/base/sounds/steps/stone_2.ogg differ diff --git a/res/content/base/sounds/steps/stone_3.ogg b/res/content/base/sounds/steps/stone_3.ogg new file mode 100644 index 00000000..87d2377f Binary files /dev/null and b/res/content/base/sounds/steps/stone_3.ogg differ diff --git a/res/content/base/sounds/steps/wood_0.ogg b/res/content/base/sounds/steps/wood_0.ogg new file mode 100644 index 00000000..4075e43a Binary files /dev/null and b/res/content/base/sounds/steps/wood_0.ogg differ diff --git a/res/content/base/sounds/steps/wood_1.ogg b/res/content/base/sounds/steps/wood_1.ogg new file mode 100644 index 00000000..4b4ab540 Binary files /dev/null and b/res/content/base/sounds/steps/wood_1.ogg differ diff --git a/res/content/base/sounds/steps/wood_2.ogg b/res/content/base/sounds/steps/wood_2.ogg new file mode 100644 index 00000000..b4be1ff6 Binary files /dev/null and b/res/content/base/sounds/steps/wood_2.ogg differ diff --git a/res/content/base/sounds/steps/wood_3.ogg b/res/content/base/sounds/steps/wood_3.ogg new file mode 100644 index 00000000..7cbbd63e Binary files /dev/null and b/res/content/base/sounds/steps/wood_3.ogg differ diff --git a/res/content/base/textures/blocks/grass_top.png b/res/content/base/textures/blocks/grass_top.png index f2e7324b..ffde1a4d 100644 Binary files a/res/content/base/textures/blocks/grass_top.png and b/res/content/base/textures/blocks/grass_top.png differ diff --git a/res/content/base/textures/blocks/metal.png b/res/content/base/textures/blocks/metal.png index fc1ac447..c1fde632 100644 Binary files a/res/content/base/textures/blocks/metal.png and b/res/content/base/textures/blocks/metal.png differ diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index fe837533..615df567 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -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(); diff --git a/src/assets/AssetsLoader.h b/src/assets/AssetsLoader.h index 736216ff..2c277418 100644 --- a/src/assets/AssetsLoader.h +++ b/src/assets/AssetsLoader.h @@ -49,6 +49,8 @@ class AssetsLoader { std::map loaders; std::queue entries; const ResPaths* paths; + + void tryAddSound(std::string name); public: AssetsLoader(Assets* assets, const ResPaths* paths); void addLoader(int tag, aloader_func func); diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index 72627dae..0b4e76a3 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -38,6 +38,11 @@ ALStream::ALStream(ALAudio* al, std::shared_ptr source, bool keepSour ALStream::~ALStream() { bindSpeaker(0); source = nullptr; + + while (!unusedBuffers.empty()) { + al->freeBuffer(unusedBuffers.front()); + unusedBuffers.pop(); + } } std::shared_ptr ALStream::getSource() const { @@ -135,6 +140,11 @@ void ALStream::update(double delta) { return; } ALSpeaker* alspeaker = dynamic_cast(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; } diff --git a/src/audio/AL/alutil.h b/src/audio/AL/alutil.h index f07dbc46..193fa0d3 100644 --- a/src/audio/AL/alutil.h +++ b/src/audio/AL/alutil.h @@ -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; } diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index b5e61b15..ebcd4e9c 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -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); diff --git a/src/audio/audio.h b/src/audio/audio.h index 61609b41..63407cb4 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -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); diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 4a847433..2652a4a5 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -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( - 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 blockDefs, - std::vector itemDefs) - : blockDefs(blockDefs), - itemDefs(itemDefs) { -} + std::vector itemDefs +) : blockDefs(blockDefs), + itemDefs(itemDefs) +{} -Content::Content(ContentIndices* indices, - std::unique_ptr drawGroups, - std::unordered_map blockDefs, - std::unordered_map itemDefs, - std::unordered_map> packs) - : blockDefs(blockDefs), - itemDefs(itemDefs), - indices(indices), - packs(std::move(packs)), - drawGroups(std::move(drawGroups)) { -} +Content::Content( + ContentIndices* indices, + std::unique_ptr drawGroups, + std::unordered_map blockDefs, + std::unordered_map itemDefs, + std::unordered_map> packs, + std::unordered_map 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& Content::getBlockMaterials() const { + return blockMaterials; +} + const std::unordered_map>& Content::getPacks() const { return packs; } diff --git a/src/content/Content.h b/src/content/Content.h index 01921b9b..1f921010 100644 --- a/src/content/Content.h +++ b/src/content/Content.h @@ -8,10 +8,10 @@ #include #include #include "../typedefs.h" +#include "../voxels/Block.h" using DrawGroups = std::set; -class Block; class ItemDef; class Content; class ContentPackRuntime; @@ -48,6 +48,8 @@ class ContentBuilder { std::unordered_map itemDefs; std::vector itemIds; + std::unordered_map blockMaterials; + std::unordered_map> 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 blockDefs; std::vector itemDefs; public: - ContentIndices(std::vector blockDefs, - std::vector itemDefs); + ContentIndices( + std::vector blockDefs, + std::vector itemDefs + ); inline Block* getBlockDef(blockid_t id) const { if (id >= blockDefs.size()) @@ -109,14 +114,18 @@ class Content { std::unordered_map itemDefs; std::unique_ptr indices; std::unordered_map> packs; + std::unordered_map blockMaterials; public: std::unique_ptr const drawGroups; - Content(ContentIndices* indices, - std::unique_ptr drawGroups, - std::unordered_map blockDefs, - std::unordered_map itemDefs, - std::unordered_map> packs); + Content( + ContentIndices* indices, + std::unique_ptr drawGroups, + std::unordered_map blockDefs, + std::unordered_map itemDefs, + std::unordered_map> packs, + std::unordered_map 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& getBlockMaterials() const; const std::unordered_map>& getPacks() const; }; diff --git a/src/content/ContentLUT.h b/src/content/ContentLUT.h index 6219c696..8b3f5886 100644 --- a/src/content/ContentLUT.h +++ b/src/content/ContentLUT.h @@ -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 blocks; std::vector blockNames; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 951e522c..146cb695 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -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)); + } + } } diff --git a/src/content/ContentLoader.h b/src/content/ContentLoader.h index 2606e71d..b176b861 100644 --- a/src/content/ContentLoader.h +++ b/src/content/ContentLoader.h @@ -1,12 +1,13 @@ #ifndef CONTENT_CONTENT_LOADER_H_ #define CONTENT_CONTENT_LOADER_H_ +#include "../voxels/Block.h" + #include #include 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); diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index 9561dfb2..493b4f80 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -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(level->content, assets)), - blocksAtlas(BlocksPreview::build(contentCache.get(), assets, level->content)) { + : level(level), + assets(assets), + contentCache(std::make_unique(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() { diff --git a/src/frontend/LevelFrontend.h b/src/frontend/LevelFrontend.h index 772c5309..ea8cbe33 100644 --- a/src/frontend/LevelFrontend.h +++ b/src/frontend/LevelFrontend.h @@ -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; diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 0fb20427..bb41880b 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -2,6 +2,7 @@ #include #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 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; diff --git a/src/frontend/screens.cpp b/src/frontend/screens.cpp index 8c12074e..41a9b838 100644 --- a/src/frontend/screens.cpp +++ b/src/frontend/screens.cpp @@ -100,6 +100,8 @@ LevelScreen::LevelScreen(Engine* engine, Level* level) : Screen(engine) { worldRenderer = std::make_unique(engine, frontend.get(), controller->getPlayer()); hud = std::make_unique(engine, frontend.get(), controller->getPlayer()); + frontend->observe(controller.get()); + backlight = settings.graphics.backlight; animator = std::make_unique(); @@ -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 ); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 4e938dec..3839928d 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -57,3 +57,7 @@ Level* LevelController::getLevel() { Player* LevelController::getPlayer() { return player->getPlayer(); } + +PlayerController* LevelController::getPlayerController() { + return player.get(); +} diff --git a/src/logic/LevelController.h b/src/logic/LevelController.h index d70d49a0..e6fefd29 100644 --- a/src/logic/LevelController.h +++ b/src/logic/LevelController.h @@ -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; @@ -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_ diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 5d1ba910..0c0daca4 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -20,10 +20,14 @@ #include "../core_defs.h" +#define _USE_MATH_DEFINES +#include + 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, 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> 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(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(0)), - camControl(player, settings.camera), - blocksController(blocksController) { + BlocksController* blocksController +) : level(level), + player(level->getObject(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); +} diff --git a/src/logic/PlayerController.h b/src/logic/PlayerController.h index 2bf3c437..3dd6a337 100644 --- a/src/logic/PlayerController.h +++ b/src/logic/PlayerController.h @@ -2,6 +2,8 @@ #define PLAYER_CONTROL_H_ #include +#include +#include #include #include "../settings.h" @@ -9,48 +11,87 @@ class Camera; class Level; +class Block; class BlocksController; class CameraControl { - std::shared_ptr player; - std::shared_ptr 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; + std::shared_ptr 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, const CameraSettings& settings); - void updateMouse(PlayerInput& input); - void update(PlayerInput& input, float delta, Chunks* chunks); - void refresh(); + CameraControl(std::shared_ptr 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; + class PlayerController { - Level* level; - std::shared_ptr player; - PlayerInput input; - CameraControl camControl; + Level* level; + std::shared_ptr 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 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_ */ diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 93b294cd..cd787113 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -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 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(position, glm::radians(90.0f))), + spCamera(std::make_shared(position, glm::radians(90.0f))), + tpCamera(std::make_shared(position, glm::radians(90.0f))), + currentCamera(camera), + hitbox(std::make_unique(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 Player::getInventory() const { @@ -163,62 +170,62 @@ std::shared_ptr 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 Player::serialize() const { - glm::vec3 position = hitbox->position; - auto root = std::make_unique(); - 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(); + 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"); diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 0b3491bd..dd4ff3bf 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -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; diff --git a/src/voxels/Block.h b/src/voxels/Block.h index d0403261..a3ee44d3 100644 --- a/src/voxels/Block.h +++ b/src/voxels/Block.h @@ -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 modelTextures = {}; std::vector modelBoxes = {}; std::vector modelExtraPoints = {}; //initially made for tetragons std::vector 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 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 hitboxes[BlockRotProfile::MAX_COUNT]; + + /// @brief set of block callbacks flags block_funcs_set funcsset {}; + + /// @brief picking item integer id itemid_t pickingItem = 0; } rt;