merge main

This commit is contained in:
@clasher113
2024-05-18 16:39:02 +03:00
413 changed files with 17869 additions and 13272 deletions
+40 -65
View File
@@ -1,112 +1,87 @@
#include "Assets.h"
#include "Assets.hpp"
#include "../audio/audio.h"
#include "../graphics/Texture.h"
#include "../graphics/Shader.h"
#include "../graphics/Atlas.h"
#include "../graphics/Font.h"
#include "../frontend/UiDocument.h"
#include "../logic/scripting/scripting.h"
#include "../audio/audio.hpp"
#include "../graphics/core/Texture.hpp"
#include "../graphics/core/Shader.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../graphics/core/Font.hpp"
#include "../frontend/UiDocument.hpp"
#include "../logic/scripting/scripting.hpp"
Assets::~Assets() {
}
Texture* Assets::getTexture(std::string name) const {
auto found = textures.find(name);
if (found == textures.end())
return nullptr;
return found->second.get();
auto found = textures.find(name);
if (found == textures.end())
return nullptr;
return found->second.get();
}
void Assets::store(Texture* texture, std::string name){
textures.emplace(name, texture);
textures.emplace(name, texture);
}
Shader* Assets::getShader(std::string name) const{
auto found = shaders.find(name);
if (found == shaders.end())
return nullptr;
return found->second.get();
auto found = shaders.find(name);
if (found == shaders.end())
return nullptr;
return found->second.get();
}
void Assets::store(Shader* shader, std::string name){
shaders.emplace(name, shader);
shaders.emplace(name, shader);
}
Font* Assets::getFont(std::string name) const {
auto found = fonts.find(name);
if (found == fonts.end())
return nullptr;
return found->second.get();
auto found = fonts.find(name);
if (found == fonts.end())
return nullptr;
return found->second.get();
}
void Assets::store(Font* font, std::string name){
fonts.emplace(name, font);
fonts.emplace(name, font);
}
Atlas* Assets::getAtlas(std::string name) const {
auto found = atlases.find(name);
if (found == atlases.end())
return nullptr;
return found->second.get();
auto found = atlases.find(name);
if (found == atlases.end())
return nullptr;
return found->second.get();
}
void Assets::store(Atlas* atlas, std::string name){
atlases.emplace(name, atlas);
atlases.emplace(name, atlas);
}
audio::Sound* Assets::getSound(std::string name) const {
auto found = sounds.find(name);
if (found == sounds.end())
return nullptr;
return found->second.get();
auto found = sounds.find(name);
if (found == sounds.end())
return nullptr;
return found->second.get();
}
void Assets::store(audio::Sound* sound, std::string name) {
sounds.emplace(name, sound);
sounds.emplace(name, sound);
}
const std::vector<TextureAnimation>& Assets::getAnimations() {
return animations;
return animations;
}
void Assets::store(const TextureAnimation& animation) {
animations.emplace_back(animation);
animations.emplace_back(animation);
}
UiDocument* Assets::getLayout(std::string name) const {
auto found = layouts.find(name);
if (found == layouts.end())
return nullptr;
return found->second.get();
auto found = layouts.find(name);
if (found == layouts.end())
return nullptr;
return found->second.get();
}
void Assets::store(UiDocument* layout, std::string name) {
layouts.emplace(name, layout);
}
void Assets::extend(const Assets& assets) {
for (auto entry : assets.textures) {
textures[entry.first] = entry.second;
}
for (auto entry : assets.shaders) {
shaders[entry.first] = entry.second;
}
for (auto entry : assets.fonts) {
fonts[entry.first] = entry.second;
}
for (auto entry : assets.atlases) {
atlases[entry.first] = entry.second;
}
for (auto entry : assets.layouts) {
layouts[entry.first] = entry.second;
}
for (auto entry : assets.sounds) {
sounds[entry.first] = entry.second;
}
animations.clear();
for (auto entry : assets.animations) {
animations.emplace_back(entry);
}
layouts[name] = std::shared_ptr<UiDocument>(layout);
}
-55
View File
@@ -1,55 +0,0 @@
#ifndef ASSETS_ASSETS_H_
#define ASSETS_ASSETS_H_
#include "../graphics/TextureAnimation.h"
#include <string>
#include <memory>
#include <unordered_map>
#include <vector>
class Texture;
class Shader;
class Font;
class Atlas;
class UiDocument;
namespace audio {
class Sound;
}
class Assets {
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
std::unordered_map<std::string, std::shared_ptr<audio::Sound>> sounds;
std::vector<TextureAnimation> animations;
public:
~Assets();
Texture* getTexture(std::string name) const;
void store(Texture* texture, std::string name);
Shader* getShader(std::string name) const;
void store(Shader* shader, std::string name);
Font* getFont(std::string name) const;
void store(Font* font, std::string name);
Atlas* getAtlas(std::string name) const;
void store(Atlas* atlas, std::string name);
audio::Sound* getSound(std::string name) const;
void store(audio::Sound* sound, std::string name);
const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name);
void extend(const Assets& assets);
};
#endif /* ASSETS_ASSETS_H_ */
+63
View File
@@ -0,0 +1,63 @@
#ifndef ASSETS_ASSETS_HPP_
#define ASSETS_ASSETS_HPP_
#include "../graphics/core/TextureAnimation.hpp"
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include <vector>
class Texture;
class Shader;
class Font;
class Atlas;
class Assets;
class UiDocument;
namespace audio {
class Sound;
}
namespace assetload {
/// @brief final work to do in the main thread
using postfunc = std::function<void(Assets*)>;
}
class Assets {
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
std::unordered_map<std::string, std::shared_ptr<audio::Sound>> sounds;
std::vector<TextureAnimation> animations;
public:
Assets() {}
Assets(const Assets&) = delete;
~Assets();
Texture* getTexture(std::string name) const;
void store(Texture* texture, std::string name);
Shader* getShader(std::string name) const;
void store(Shader* shader, std::string name);
Font* getFont(std::string name) const;
void store(Font* font, std::string name);
Atlas* getAtlas(std::string name) const;
void store(Atlas* atlas, std::string name);
audio::Sound* getSound(std::string name) const;
void store(audio::Sound* sound, std::string name);
const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name);
};
#endif // ASSETS_ASSETS_HPP_
+113 -39
View File
@@ -1,58 +1,74 @@
#include "AssetsLoader.h"
#include "Assets.h"
#include "AssetsLoader.hpp"
#include "assetload_funcs.h"
#include "Assets.hpp"
#include "assetload_funcs.hpp"
#include "../util/ThreadPool.hpp"
#include "../constants.hpp"
#include "../data/dynamic.hpp"
#include "../debug/Logger.hpp"
#include "../coders/imageio.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include "../content/Content.hpp"
#include "../content/ContentPack.hpp"
#include "../graphics/core/Texture.hpp"
#include "../logic/scripting/scripting.hpp"
#include <iostream>
#include <memory>
#include "../constants.h"
#include "../data/dynamic.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
#include "../content/Content.h"
#include "../content/ContentPack.h"
#include "../logic/scripting/scripting.h"
static debug::Logger logger("assets-loader");
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
: assets(assets), paths(paths)
{
addLoader(AssetType::shader, assetload::shader);
addLoader(AssetType::texture, assetload::texture);
addLoader(AssetType::font, assetload::font);
addLoader(AssetType::atlas, assetload::atlas);
addLoader(AssetType::shader, assetload::shader);
addLoader(AssetType::texture, assetload::texture);
addLoader(AssetType::font, assetload::font);
addLoader(AssetType::atlas, assetload::atlas);
addLoader(AssetType::layout, assetload::layout);
addLoader(AssetType::sound, assetload::sound);
}
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
loaders[tag] = func;
loaders[tag] = func;
}
void AssetsLoader::add(AssetType tag, const std::string filename, const std::string alias, std::shared_ptr<AssetCfg> settings) {
entries.push(aloader_entry{tag, filename, alias, settings});
entries.push(aloader_entry{tag, filename, alias, settings});
}
bool AssetsLoader::hasNext() const {
return !entries.empty();
return !entries.empty();
}
aloader_func AssetsLoader::getLoader(AssetType tag) {
auto found = loaders.find(tag);
if (found == loaders.end()) {
throw std::runtime_error(
"unknown asset tag "+std::to_string(static_cast<int>(tag))
);
}
return found->second;
}
bool AssetsLoader::loadNext() {
const aloader_entry& entry = entries.front();
std::cout << " loading " << entry.filename << " as " << entry.alias << std::endl;
std::cout.flush();
auto found = loaders.find(entry.tag);
if (found == loaders.end()) {
std::cerr << "unknown asset tag " << static_cast<int>(entry.tag) << std::endl;
return false;
}
aloader_func loader = found->second;
bool status = loader(*this, assets, paths, entry.filename, entry.alias, entry.config);
entries.pop();
return status;
const aloader_entry& entry = entries.front();
logger.info() << "loading " << entry.filename << " as " << entry.alias;
try {
aloader_func loader = getLoader(entry.tag);
auto postfunc = loader(this, paths, entry.filename, entry.alias, entry.config);
postfunc(assets);
entries.pop();
return true;
} catch (std::runtime_error& err) {
logger.error() << err.what();
entries.pop();
return false;
}
}
void addLayouts(int env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) {
void addLayouts(scriptenv env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) {
if (!fs::is_directory(folder)) {
return;
}
@@ -100,7 +116,7 @@ void AssetsLoader::processPreload(
switch (tag) {
case AssetType::sound:
add(tag, path, name, std::make_shared<SoundCfg>(
map->getBool("keep-pcm", false)
map->get("keep-pcm", false)
));
break;
default:
@@ -115,13 +131,14 @@ void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) {
}
for (uint i = 0; i < list->size(); i++) {
auto value = list->get(i);
switch (value->type) {
case dynamic::valtype::string:
processPreload(tag, *value->value.str, nullptr);
switch (static_cast<dynamic::Type>(value.index())) {
case dynamic::Type::string:
processPreload(tag, std::get<std::string>(value), nullptr);
break;
case dynamic::valtype::map: {
auto name = value->value.map->getStr("name");
processPreload(tag, name, value->value.map);
case dynamic::Type::map: {
auto map = std::get<dynamic::Map_sptr>(value);
auto name = map->get<std::string>("name");
processPreload(tag, name, map.get());
break;
}
default:
@@ -161,9 +178,11 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_world_icon", "gui/no_world_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/warning", "gui/warning");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/error", "gui/error");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/cross", "gui/cross");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh");
if (content) {
loader.processPreloadConfigs(content);
@@ -179,13 +198,68 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
auto pack = entry.second.get();
auto& info = pack->getInfo();
fs::path folder = info.folder / fs::path("layouts");
addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader);
addLayouts(pack->getEnvironment(), info.id, folder, loader);
}
}
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks");
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items");
}
bool AssetsLoader::loadExternalTexture(
Assets* assets,
const std::string& name,
std::vector<std::filesystem::path> alternatives
) {
if (assets->getTexture(name) != nullptr) {
return true;
}
for (auto& path : alternatives) {
if (fs::exists(path)) {
try {
auto image = imageio::read(path.string());
assets->store(Texture::from(image.get()).release(), name);
return true;
} catch (const std::exception& err) {
logger.error() << "error while loading external "
<< path.u8string() << ": " << err.what();
}
}
}
return false;
}
const ResPaths* AssetsLoader::getPaths() const {
return paths;
return paths;
}
class LoaderWorker : public util::Worker<aloader_entry, assetload::postfunc> {
AssetsLoader* loader;
public:
LoaderWorker(AssetsLoader* loader) : loader(loader) {
}
assetload::postfunc operator()(const std::shared_ptr<aloader_entry>& entry) override {
aloader_func loadfunc = loader->getLoader(entry->tag);
return loadfunc(loader, loader->getPaths(), entry->filename, entry->alias, entry->config);
}
};
std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) {
auto pool = std::make_shared<
util::ThreadPool<aloader_entry, assetload::postfunc>
>(
"assets-loader-pool",
[=](){return std::make_shared<LoaderWorker>(this);},
[=](assetload::postfunc& func) {
func(assets);
}
);
pool->setOnComplete(onDone);
while (!entries.empty()) {
const aloader_entry& entry = entries.front();
auto ptr = std::make_shared<aloader_entry>(entry);
pool->enqueueJob(ptr);
entries.pop();
}
return pool;
}
-94
View File
@@ -1,94 +0,0 @@
#ifndef ASSETS_ASSETS_LOADER_H
#define ASSETS_ASSETS_LOADER_H
#include <string>
#include <memory>
#include <filesystem>
#include <functional>
#include <map>
#include <queue>
namespace dynamic {
class Map;
class List;
}
enum class AssetType {
texture,
shader,
font,
atlas,
layout,
sound
};
class ResPaths;
class Assets;
class AssetsLoader;
class Content;
struct AssetCfg {
virtual ~AssetCfg() {}
};
struct LayoutCfg : AssetCfg {
int env;
LayoutCfg(int env) : env(env) {}
};
struct SoundCfg : AssetCfg {
bool keepPCM;
SoundCfg(bool keepPCM) : keepPCM(keepPCM) {}
};
using aloader_func = std::function<bool(AssetsLoader&, Assets*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
struct aloader_entry {
AssetType tag;
const std::string filename;
const std::string alias;
std::shared_ptr<AssetCfg> config;
};
class AssetsLoader {
Assets* assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
const ResPaths* paths;
void tryAddSound(std::string name);
void processPreload(AssetType tag, const std::string& name, dynamic::Map* map);
void processPreloadList(AssetType tag, dynamic::List* list);
void processPreloadConfig(std::filesystem::path file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
/// @param tag asset type
/// @param filename asset file path
/// @param alias internal asset name
/// @param settings asset loading settings (based on asset type)
void add(
AssetType tag,
const std::string filename,
const std::string alias,
std::shared_ptr<AssetCfg> settings=nullptr
);
bool hasNext() const;
bool loadNext();
/// @brief Enqueue core and content assets
/// @param loader target loader
/// @param content engine content
static void addDefaults(AssetsLoader& loader, const Content* content);
const ResPaths* getPaths() const;
};
#endif // ASSETS_ASSETS_LOADER_H
+113
View File
@@ -0,0 +1,113 @@
#ifndef ASSETS_ASSETS_LOADER_HPP_
#define ASSETS_ASSETS_LOADER_HPP_
#include "Assets.hpp"
#include "../interfaces/Task.hpp"
#include "../typedefs.hpp"
#include "../delegates.hpp"
#include <string>
#include <memory>
#include <filesystem>
#include <functional>
#include <map>
#include <queue>
namespace dynamic {
class Map;
class List;
}
enum class AssetType {
texture,
shader,
font,
atlas,
layout,
sound
};
class ResPaths;
class AssetsLoader;
class Content;
struct AssetCfg {
virtual ~AssetCfg() {}
};
struct LayoutCfg : AssetCfg {
scriptenv env;
LayoutCfg(scriptenv env) : env(env) {}
};
struct SoundCfg : AssetCfg {
bool keepPCM;
SoundCfg(bool keepPCM) : keepPCM(keepPCM) {}
};
using aloader_func = std::function<assetload::postfunc(
AssetsLoader*, // redundant?
const ResPaths*,
const std::string&,
const std::string&,
std::shared_ptr<AssetCfg>)
>;
struct aloader_entry {
AssetType tag;
const std::string filename;
const std::string alias;
std::shared_ptr<AssetCfg> config;
};
class AssetsLoader {
Assets* assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
const ResPaths* paths;
void tryAddSound(std::string name);
void processPreload(AssetType tag, const std::string& name, dynamic::Map* map);
void processPreloadList(AssetType tag, dynamic::List* list);
void processPreloadConfig(std::filesystem::path file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
/// @param tag asset type
/// @param filename asset file path
/// @param alias internal asset name
/// @param settings asset loading settings (based on asset type)
void add(
AssetType tag,
const std::string filename,
const std::string alias,
std::shared_ptr<AssetCfg> settings=nullptr
);
bool hasNext() const;
bool loadNext();
std::shared_ptr<Task> startTask(runnable onDone);
const ResPaths* getPaths() const;
aloader_func getLoader(AssetType tag);
/// @brief Enqueue core and content assets
/// @param loader target loader
/// @param content engine content
static void addDefaults(AssetsLoader& loader, const Content* content);
static bool loadExternalTexture(
Assets* assets,
const std::string& name,
std::vector<std::filesystem::path> alternatives
);
};
#endif // ASSETS_ASSETS_LOADER_HPP_
+209 -203
View File
@@ -1,55 +1,54 @@
#include "assetload_funcs.h"
#include "assetload_funcs.hpp"
#include "Assets.hpp"
#include "AssetsLoader.hpp"
#include "../data/dynamic.hpp"
#include "../audio/audio.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include "../coders/imageio.hpp"
#include "../coders/json.hpp"
#include "../coders/GLSLExtension.hpp"
#include "../graphics/core/Shader.hpp"
#include "../graphics/core/Texture.hpp"
#include "../graphics/core/ImageData.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../graphics/core/Font.hpp"
#include "../graphics/core/TextureAnimation.hpp"
#include "../frontend/UiDocument.hpp"
#include <iostream>
#include <stdexcept>
#include <filesystem>
#include "Assets.h"
#include "AssetsLoader.h"
#include "../audio/audio.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
#include "../coders/png.h"
#include "../coders/json.h"
#include "../graphics/Shader.h"
#include "../graphics/Texture.h"
#include "../graphics/ImageData.h"
#include "../graphics/Atlas.h"
#include "../graphics/Font.h"
#include "../graphics/TextureAnimation.h"
#include "../frontend/UiDocument.h"
#include "../logic/scripting/scripting.h"
namespace fs = std::filesystem;
static bool animation(
Assets* assets,
const ResPaths* paths,
const std::string directory,
const std::string name,
Assets* assets,
const ResPaths* paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
Atlas* dstAtlas
);
bool assetload::texture(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::texture(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg>
) {
std::unique_ptr<Texture> texture(
png::load_texture(paths->find(filename+".png").u8string())
std::shared_ptr<ImageData> image (
imageio::read(paths->find(filename+".png").u8string()).release()
);
if (texture == nullptr) {
std::cerr << "failed to load texture '" << name << "'" << std::endl;
return false;
}
assets->store(texture.release(), name);
return true;
return [name, image](auto assets) {
assets->store(Texture::from(image.get()).release(), name);
};
}
bool assetload::shader(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::shader(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
@@ -61,43 +60,32 @@ bool assetload::shader(
std::string vertexSource = files::read_string(vertexFile);
std::string fragmentSource = files::read_string(fragmentFile);
Shader* shader = Shader::create(
vertexFile.string(),
fragmentFile.string(),
vertexSource, fragmentSource
);
vertexSource = Shader::preprocessor->process(vertexFile, vertexSource);
fragmentSource = Shader::preprocessor->process(fragmentFile, fragmentSource);
if (shader == nullptr) {
std::cerr << "failed to load shader '" << name << "'" << std::endl;
return false;
}
assets->store(shader, name);
return true;
return [=](auto assets) {
assets->store(Shader::create(
vertexFile.u8string(),
fragmentFile.u8string(),
vertexSource, fragmentSource
), name);
};
}
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) {
// png is only supported format
if (file.extension() != ".png")
return false;
static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
std::string name = file.stem().string();
// skip duplicates
if (atlas.has(name)) {
return false;
}
std::unique_ptr<ImageData> image(png::load_image(file.string()));
if (image == nullptr) {
std::cerr << "could not to load " << file.string() << std::endl;
return false;
}
auto image = imageio::read(file.string());
image->fixAlphaColor();
atlas.add(name, image.release());
atlas.add(name, std::move(image));
return true;
}
bool assetload::atlas(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::atlas(
AssetsLoader*,
const ResPaths* paths,
const std::string directory,
const std::string name,
@@ -105,201 +93,219 @@ bool assetload::atlas(
) {
AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) {
if (!appendAtlas(builder, file)) continue;
if (!imageio::is_read_supported(file.extension().u8string()))
continue;
if (!append_atlas(builder, file))
continue;
}
Atlas* atlas = builder.build(2);
assets->store(atlas, name);
for (const auto& file : builder.getNames()) {
animation(assets, paths, "textures", file, atlas);
}
return true;
std::set<std::string> names = builder.getNames();
Atlas* atlas = builder.build(2, false).release();
return [=](auto assets) {
atlas->prepare();
assets->store(atlas, name);
for (const auto& file : names) {
animation(assets, paths, name, directory, file, atlas);
}
};
}
bool assetload::font(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::font(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg>
) {
std::vector<std::unique_ptr<Texture>> pages;
auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>();
for (size_t i = 0; i <= 4; i++) {
std::string name = filename + "_" + std::to_string(i) + ".png";
name = paths->find(name).string();
std::unique_ptr<Texture> texture (png::load_texture(name));
if (texture == nullptr) {
std::cerr << "failed to load bitmap font '" << name;
std::cerr << "' (missing page " << std::to_string(i) << ")";
std::cerr << std::endl;
return false;
}
pages.push_back(std::move(texture));
pages->push_back(imageio::read(name));
}
int res = pages[0]->getHeight() / 16;
assets->store(new Font(std::move(pages), res, 4), name);
return true;
return [=](auto assets) {
int res = pages->at(0)->getHeight() / 16;
std::vector<std::unique_ptr<Texture>> textures;
for (auto& page : *pages) {
textures.emplace_back(Texture::from(page.get()));
}
assets->store(new Font(std::move(textures), res, 4), name);
};
}
bool assetload::layout(
AssetsLoader& loader,
Assets* assets,
assetload::postfunc assetload::layout(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
std::shared_ptr<AssetCfg> config
) {
try {
auto cfg = dynamic_cast<LayoutCfg*>(config.get());
auto document = UiDocument::read(loader, cfg->env, name, file);
assets->store(document.release(), name);
return true;
} catch (const parsing_error& err) {
std::cerr << "failed to parse layout XML '" << file << "'" << std::endl;
std::cerr << err.errorLog() << std::endl;
return false;
}
return [=](auto assets) {
try {
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
auto document = UiDocument::read(cfg->env, name, file);
assets->store(document.release(), name);
} catch (const parsing_error& err) {
throw std::runtime_error(
"failed to parse layout XML '"+file+"':\n"+err.errorLog()
);
}
};
}
bool assetload::sound(
AssetsLoader& loader,
Assets* assets,
assetload::postfunc assetload::sound(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
std::shared_ptr<AssetCfg> config
) {
auto cfg = dynamic_cast<SoundCfg*>(config.get());
auto cfg = std::dynamic_pointer_cast<SoundCfg>(config);
bool keepPCM = cfg ? cfg->keepPCM : false;
std::string extension = ".ogg";
try {
std::unique_ptr<audio::Sound> baseSound = nullptr;
std::unique_ptr<audio::Sound> baseSound = nullptr;
// looking for 'sound_name' as base sound
auto soundFile = paths->find(file+extension);
if (fs::exists(soundFile)) {
baseSound.reset(audio::load_sound(soundFile, keepPCM));
}
// looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file+"_0"+extension);
if (fs::exists(variantFile)) {
baseSound.reset(audio::load_sound(variantFile, keepPCM));
}
// loading sound variants
for (uint i = 1; ; i++) {
auto variantFile = paths->find(file+"_"+std::to_string(i)+extension);
if (!fs::exists(variantFile)) {
break;
}
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
}
assets->store(baseSound.release(), name);
}
catch (std::runtime_error& err) {
std::cerr << err.what() << std::endl;
return false;
// looking for 'sound_name' as base sound
auto soundFile = paths->find(file+extension);
if (fs::exists(soundFile)) {
baseSound = audio::load_sound(soundFile, keepPCM);
}
return true;
// looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file+"_0"+extension);
if (fs::exists(variantFile)) {
baseSound = audio::load_sound(variantFile, keepPCM);
}
// loading sound variants
for (uint i = 1; ; i++) {
auto variantFile = paths->find(file+"_"+std::to_string(i)+extension);
if (!fs::exists(variantFile)) {
break;
}
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
}
if (baseSound == nullptr) {
throw std::runtime_error("could not to find sound: " + file);
}
auto sound = baseSound.release();
return [=](auto assets) {
assets->store(sound, name);
};
}
static void read_anim_file(
const std::string& animFile,
std::vector<std::pair<std::string, int>>& frameList
) {
auto root = files::read_json(animFile);
auto frameArr = root->list("frames");
float frameDuration = DEFAULT_FRAME_DURATION;
std::string frameName;
if (frameArr) {
for (size_t i = 0; i < frameArr->size(); i++) {
auto currentFrame = frameArr->list(i);
frameName = currentFrame->str(0);
if (currentFrame->size() > 1) {
frameDuration = currentFrame->integer(1);
}
frameList.emplace_back(frameName, frameDuration);
}
}
}
static TextureAnimation create_animation(
Atlas* srcAtlas,
Atlas* dstAtlas,
const std::string& name,
const std::set<std::string>& frameNames,
const std::vector<std::pair<std::string, int>>& frameList
) {
Texture* srcTex = srcAtlas->getTexture();
Texture* dstTex = dstAtlas->getTexture();
UVRegion region = dstAtlas->get(name);
TextureAnimation animation(srcTex, dstTex);
Frame frame;
uint dstWidth = dstTex->getWidth();
uint dstHeight = dstTex->getHeight();
uint srcWidth = srcTex->getWidth();
uint srcHeight = srcTex->getHeight();
frame.dstPos = glm::ivec2(region.u1 * dstWidth, region.v1 * dstHeight);
frame.size = glm::ivec2(region.u2 * dstWidth, region.v2 * dstHeight) - frame.dstPos;
for (const auto& elem : frameList) {
if (!srcAtlas->has(elem.first)) {
std::cerr << "Unknown frame name: " << elem.first << std::endl;
continue;
}
region = srcAtlas->get(elem.first);
if (elem.second > 0) {
frame.duration = static_cast<float>(elem.second) / 1000.0f;
}
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
}
return animation;
}
inline bool contains(
const std::vector<std::pair<std::string, int>>& frameList,
const std::string& frameName
) {
for (const auto& elem : frameList) {
if (frameName == elem.first) {
return true;
}
}
return false;
}
static bool animation(
Assets* assets,
const ResPaths* paths,
const std::string directory,
const std::string name,
const ResPaths* paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
Atlas* dstAtlas
) {
std::string animsDir = directory + "/animations";
std::string blocksDir = directory + "/blocks";
std::string animsDir = directory + "/animation";
for (const auto& folder : paths->listdir(animsDir)) {
if (!fs::is_directory(folder)) continue;
if (folder.filename().string() != name) continue;
if (folder.filename().u8string() != name) continue;
if (fs::is_empty(folder)) continue;
AtlasBuilder builder;
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png"));
std::string animFile = folder.string() + "/animation.json";
std::vector<std::pair<std::string, float>> frameList;
append_atlas(builder, paths->find(directory + "/" + name + ".png"));
std::vector<std::pair<std::string, int>> frameList;
std::string animFile = folder.u8string() + "/animation.json";
if (fs::exists(animFile)) {
auto root = files::read_json(animFile);
auto frameArr = root->list("frames");
float frameDuration = DEFAULT_FRAME_DURATION;
std::string frameName;
if (frameArr) {
for (size_t i = 0; i < frameArr->size(); i++) {
auto currentFrame = frameArr->list(i);
frameName = currentFrame->str(0);
if (currentFrame->size() > 1)
frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
frameList.emplace_back(frameName, frameDuration);
}
}
read_anim_file(animFile, frameList);
}
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
if (!frameList.empty()) {
bool contains = false;
for (const auto& elem : frameList) {
if (file.stem() == elem.first) {
contains = true;
break;
}
}
if (!contains) continue;
if (!frameList.empty() && !contains(frameList, file.stem().u8string())) {
continue;
}
if (!appendAtlas(builder, file)) continue;
if (!append_atlas(builder, file))
continue;
}
std::unique_ptr<Atlas> srcAtlas (builder.build(2));
Texture* srcTex = srcAtlas->getTexture();
Texture* dstTex = dstAtlas->getTexture();
TextureAnimation animation(srcTex, dstTex);
Frame frame;
UVRegion region = dstAtlas->get(name);
uint dstWidth = dstTex->getWidth();
uint dstHeight = dstTex->getHeight();
uint srcWidth = srcTex->getWidth();
uint srcHeight = srcTex->getHeight();
frame.dstPos = glm::ivec2(region.u1 * dstWidth, region.v1 * dstHeight);
frame.size = glm::ivec2(region.u2 * dstWidth, region.v2 * dstHeight) - frame.dstPos;
auto srcAtlas = builder.build(2, true);
if (frameList.empty()) {
for (const auto& elem : builder.getNames()) {
region = srcAtlas->get(elem);
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
for (const auto& frameName : builder.getNames()) {
frameList.emplace_back(frameName, 0);
}
}
else {
for (const auto& elem : frameList) {
if (!srcAtlas->has(elem.first)) {
std::cerr << "Unknown frame name: " << elem.first << std::endl;
continue;
}
region = srcAtlas->get(elem.first);
frame.duration = elem.second;
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
}
}
assets->store(srcAtlas.release(), name + "_animation");
auto animation = create_animation(
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
);
assets->store(srcAtlas.release(), atlasName + "/" + name + "_animation");
assets->store(animation);
return true;
}
return true;
@@ -1,5 +1,7 @@
#ifndef ASSETS_ASSET_LOADERS_H_
#define ASSETS_ASSET_LOADERS_H_
#ifndef ASSETS_ASSET_LOADERS_HPP_
#define ASSETS_ASSET_LOADERS_HPP_
#include "Assets.hpp"
#include <string>
#include <memory>
@@ -12,50 +14,44 @@ struct AssetCfg;
/// @brief see AssetsLoader.h: aloader_func
namespace assetload {
bool texture(
AssetsLoader&,
Assets*,
postfunc texture(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool shader(
AssetsLoader&,
Assets*,
postfunc shader(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool atlas(
AssetsLoader&,
Assets*,
postfunc atlas(
AssetsLoader*,
const ResPaths* paths,
const std::string directory,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool font(
AssetsLoader&,
Assets*,
postfunc font(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool layout(
AssetsLoader&,
Assets*,
postfunc layout(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool sound(
AssetsLoader&,
Assets*,
postfunc sound(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
@@ -63,4 +59,4 @@ namespace assetload {
);
}
#endif // ASSETS_ASSET_LOADERS_H_
#endif // ASSETS_ASSET_LOADERS_HPP_
+26 -21
View File
@@ -1,7 +1,11 @@
#include "ALAudio.h"
#include "alutil.h"
#include "ALAudio.hpp"
#include "alutil.hpp"
#include "../../debug/Logger.hpp"
#include <string>
#include <iostream>
static debug::Logger logger("al-audio");
using namespace audio;
@@ -19,14 +23,14 @@ ALSound::~ALSound() {
buffer = 0;
}
Speaker* ALSound::newInstance(int priority, int channel) const {
std::unique_ptr<Speaker> ALSound::newInstance(int priority, int channel) const {
uint source = al->getFreeSource();
if (source == 0) {
return nullptr;
}
AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
auto speaker = new ALSpeaker(al, source, priority, channel);
auto speaker = std::make_unique<ALSpeaker>(al, source, priority, channel);
speaker->duration = duration;
return speaker;
}
@@ -62,7 +66,7 @@ bool ALStream::preloadBuffer(uint buffer, bool loop) {
return true;
}
Speaker* ALStream::createSpeaker(bool loop, int channel) {
std::unique_ptr<Speaker> ALStream::createSpeaker(bool loop, int channel) {
this->loop = loop;
uint source = al->getFreeSource();
if (source == 0) {
@@ -75,7 +79,7 @@ Speaker* ALStream::createSpeaker(bool loop, int channel) {
}
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
}
return new ALSpeaker(al, source, PRIORITY_HIGH, channel);
return std::make_unique<ALSpeaker>(al, source, PRIORITY_HIGH, channel);
}
@@ -203,10 +207,10 @@ ALSpeaker::~ALSpeaker() {
}
}
void ALSpeaker::update(const Channel* channel, float masterVolume) {
void ALSpeaker::update(const Channel* channel) {
if (source == 0)
return;
float gain = this->volume * channel->getVolume()*masterVolume;
float gain = this->volume * channel->getVolume();
AL_CHECK(alSourcef(source, AL_GAIN, gain));
if (!paused) {
@@ -259,7 +263,7 @@ void ALSpeaker::play() {
paused = false;
stopped = false;
auto channel = get_channel(this->channel);
AL_CHECK(alSourcef(source, AL_GAIN, volume * channel->getVolume()));
AL_CHECK(alSourcef(source, AL_GAIN, volume * channel->getVolume() * get_channel(0)->getVolume()));
AL_CHECK(alSourcePlay(source));
}
@@ -341,14 +345,14 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
alcGetIntegerv(device, ALC_ALL_ATTRIBUTES, size, &attrs[0]);
for (size_t i = 0; i < attrs.size(); ++i){
if (attrs[i] == ALC_MONO_SOURCES) {
std::cout << "AL: max mono sources: " << attrs[i+1] << std::endl;
logger.info() << "max mono sources: " << attrs[i+1];
maxSources = attrs[i+1];
}
}
auto devices = getAvailableDevices();
std::cout << "AL devices:" << std::endl;
logger.info() << "devices:";
for (auto& name : devices) {
std::cout << " " << name << std::endl;
logger.info() << " " << name;
}
}
@@ -368,21 +372,21 @@ ALAudio::~ALAudio() {
AL_CHECK(alcMakeContextCurrent(context));
alcDestroyContext(context);
if (!alcCloseDevice(device)) {
std::cerr << "AL: device not closed!" << std::endl;
logger.error() << "device not closed!";
}
device = nullptr;
context = nullptr;
}
Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
std::unique_ptr<Sound> ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
auto format = AL::to_al_format(pcm->channels, pcm->bitsPerSample);
uint buffer = getFreeBuffer();
AL_CHECK(alBufferData(buffer, format, pcm->data.data(), pcm->data.size(), pcm->sampleRate));
return new ALSound(this, buffer, pcm, keepPCM);
return std::make_unique<ALSound>(this, buffer, pcm, keepPCM);
}
Stream* ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new ALStream(this, stream, keepSource);
std::unique_ptr<Stream> ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return std::make_unique<ALStream>(this, stream, keepSource);
}
ALAudio* ALAudio::create() {
@@ -395,7 +399,7 @@ ALAudio* ALAudio::create() {
return nullptr;
}
AL_CHECK();
std::cout << "AL: initialized" << std::endl;
logger.info() << "initialized";
return new ALAudio(device, context);
}
@@ -406,7 +410,7 @@ uint ALAudio::getFreeSource(){
return source;
}
if (allsources.size() == maxSources){
std::cerr << "attempted to create new source, but limit is " << maxSources << std::endl;
logger.error() << "attempted to create new source, but limit is " << maxSources;
return 0;
}
ALuint id;
@@ -467,7 +471,8 @@ void ALAudio::setListener(glm::vec3 position, glm::vec3 velocity, glm::vec3 at,
AL_CHECK(alListener3f(AL_POSITION, position.x, position.y, position.z));
AL_CHECK(alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z));
AL_CHECK(alListenerfv(AL_ORIENTATION, listenerOri));
AL_CHECK(alListenerf(AL_GAIN, get_channel(0)->getVolume()));
}
void ALAudio::update(double delta) {
void ALAudio::update(double) {
}
@@ -1,5 +1,8 @@
#ifndef SRC_AUDIO_AUDIO_H_
#define SRC_AUDIO_AUDIO_H_
#ifndef SRC_AUDIO_AUDIO_HPP_
#define SRC_AUDIO_AUDIO_HPP_
#include "../audio.hpp"
#include "../../typedefs.hpp"
#include <queue>
#include <vector>
@@ -15,9 +18,6 @@
#include <AL/alc.h>
#endif
#include "../audio.h"
#include "../../typedefs.h"
namespace audio {
struct ALBuffer;
class ALAudio;
@@ -40,7 +40,7 @@ namespace audio {
return pcm;
}
Speaker* newInstance(int priority, int channel) const override;
std::unique_ptr<Speaker> newInstance(int priority, int channel) const override;
};
class ALStream : public Stream {
@@ -65,7 +65,7 @@ namespace audio {
std::shared_ptr<PCMStream> getSource() const override;
void bindSpeaker(speakerid_t speaker) override;
Speaker* createSpeaker(bool loop, int channel) override;
std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) override;
speakerid_t getSpeaker() const override;
void update(double delta) override;
@@ -91,7 +91,7 @@ namespace audio {
ALSpeaker(ALAudio* al, uint source, int priority, int channel);
~ALSpeaker();
void update(const Channel* channel, float masterVolume) override;
void update(const Channel* channel) override;
int getChannel() const override;
State getState() const override;
@@ -135,7 +135,7 @@ namespace audio {
std::vector<uint> allbuffers;
std::vector<uint> freebuffers;
uint maxSources;
uint maxSources = 256;
ALAudio(ALCdevice* device, ALCcontext* context);
public:
@@ -148,8 +148,8 @@ namespace audio {
std::vector<std::string> getAvailableDevices() const;
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
glm::vec3 position,
@@ -168,4 +168,4 @@ namespace audio {
};
}
#endif /* SRC_AUDIO_AUDIO_H_ */
#endif // SRC_AUDIO_AUDIO_HPP_
+12 -10
View File
@@ -1,6 +1,7 @@
#include "alutil.h"
#include "alutil.hpp"
#include "../../debug/Logger.hpp"
#include <iostream>
#include <fstream>
#include <cstring>
#include <memory>
@@ -14,30 +15,31 @@
#include <AL/alc.h>
#endif
static debug::Logger logger("open-al");
bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){
ALenum error = alGetError();
if(error != AL_NO_ERROR){
std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ;
logger.error() << filename << ": " << line;
switch(error){
case AL_INVALID_NAME:
std::cerr << "AL_INVALID_NAME: a bad name (ID) was passed to an OpenAL function";
logger.error() << "a bad name (ID) was passed to an OpenAL function";
break;
case AL_INVALID_ENUM:
std::cerr << "AL_INVALID_ENUM: an invalid enum value was passed to an OpenAL function";
logger.error() << "an invalid enum value was passed to an OpenAL function";
break;
case AL_INVALID_VALUE:
std::cerr << "AL_INVALID_VALUE: an invalid value was passed to an OpenAL function";
logger.error() << "an invalid value was passed to an OpenAL function";
break;
case AL_INVALID_OPERATION:
std::cerr << "AL_INVALID_OPERATION: the requested operation is not valid";
logger.error() << "the requested operation is not valid";
break;
case AL_OUT_OF_MEMORY:
std::cerr << "AL_OUT_OF_MEMORY: the requested operation resulted in OpenAL running out of memory";
logger.error() << "the requested operation resulted in OpenAL running out of memory";
break;
default:
std::cerr << "UNKNOWN AL ERROR: " << error;
logger.error() << "UNKNOWN AL ERROR: " << error;
}
std::cerr << std::endl;
return false;
}
return true;
@@ -1,5 +1,7 @@
#ifndef SRC_AUDIO_AUDIOUTIL_H_
#define SRC_AUDIO_AUDIOUTIL_H_
#ifndef AUDIO_AUDIOUTIL_HPP_
#define AUDIO_AUDIOUTIL_HPP_
#include "../../typedefs.hpp"
#include <string>
#include <type_traits>
@@ -12,7 +14,6 @@
#endif
#include <glm/glm.hpp>
#include "../../typedefs.h"
#define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__)
#define AL_GET_ERROR() AL::check_errors(__FILE__, __LINE__)
@@ -80,4 +81,4 @@ namespace AL {
}
}
#endif /* SRC_AUDIO_AUDIOUTIL_H_ */
#endif // AUDIO_AUDIOUTIL_HPP_
+5 -5
View File
@@ -1,4 +1,4 @@
#include "NoAudio.h"
#include "NoAudio.hpp"
using namespace audio;
@@ -9,12 +9,12 @@ NoSound::NoSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
}
}
Sound* NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return new NoSound(pcm, keepPCM);
std::unique_ptr<Sound> NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return std::make_unique<NoSound>(pcm, keepPCM);
}
Stream* NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new NoStream(stream, keepSource);
std::unique_ptr<Stream> NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return std::make_unique<NoStream>(stream, keepSource);
}
NoAudio* NoAudio::create() {
@@ -1,7 +1,7 @@
#ifndef AUDIO_NOAUDIO_H_
#define AUDIO_NOAUDIO_H_
#ifndef AUDIO_NOAUDIO_HPP_
#define AUDIO_NOAUDIO_HPP_
#include "audio.h"
#include "audio.hpp"
namespace audio {
class NoSound : public Sound {
@@ -19,7 +19,7 @@ namespace audio {
return pcm;
}
Speaker* newInstance(int priority, int channel) const override {
std::unique_ptr<Speaker> newInstance(int priority, int channel) const override {
return nullptr;
}
};
@@ -42,7 +42,7 @@ namespace audio {
void bindSpeaker(speakerid_t speaker) override {
}
Speaker* createSpeaker(bool loop, int channel) override{
std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) override {
return nullptr;
}
@@ -65,8 +65,8 @@ namespace audio {
public:
~NoAudio() {}
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
glm::vec3 position,
@@ -85,4 +85,4 @@ namespace audio {
};
}
#endif // AUDIO_NOAUDIO_H_
#endif // AUDIO_NOAUDIO_HPP_
+28 -30
View File
@@ -1,14 +1,14 @@
#include "audio.h"
#include "audio.hpp"
#include "NoAudio.hpp"
#include "AL/ALAudio.hpp"
#include "../coders/wav.hpp"
#include "../coders/ogg.hpp"
#include <iostream>
#include <stdexcept>
#include "NoAudio.h"
#include "AL/ALAudio.h"
#include "../coders/wav.h"
#include "../coders/ogg.h"
namespace audio {
static speakerid_t nextId = 1;
static Backend* backend;
@@ -72,9 +72,6 @@ size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
if (loop) {
seek(0);
}
if (bufferSize == 0) {
return size;
}
} while (loop);
return size;
}
@@ -94,7 +91,7 @@ public:
seekable(seekable)
{}
size_t read(char* buffer, size_t bufferSize) override {
size_t read(char*, size_t bufferSize) override {
if (closed) {
return 0;
}
@@ -159,7 +156,7 @@ void audio::initialize(bool enabled) {
create_channel("master");
}
PCM* audio::load_PCM(const fs::path& file, bool headerOnly) {
std::unique_ptr<PCM> audio::load_PCM(const fs::path& file, bool headerOnly) {
if (!fs::exists(file)) {
throw std::runtime_error("file not found '"+file.u8string()+"'");
}
@@ -172,16 +169,16 @@ PCM* audio::load_PCM(const fs::path& file, bool headerOnly) {
throw std::runtime_error("unsupported audio format");
}
Sound* audio::load_sound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()));
std::unique_ptr<Sound> audio::load_sound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()).release());
return create_sound(pcm, keepPCM);
}
Sound* audio::create_sound(std::shared_ptr<PCM> pcm, bool keepPCM) {
std::unique_ptr<Sound> audio::create_sound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return backend->createSound(pcm, keepPCM);
}
PCMStream* audio::open_PCM_stream(const fs::path& file) {
std::unique_ptr<PCMStream> audio::open_PCM_stream(const fs::path& file) {
std::string ext = file.extension().u8string();
if (ext == ".wav" || ext == ".WAV") {
return wav::create_stream(file);
@@ -191,7 +188,7 @@ PCMStream* audio::open_PCM_stream(const fs::path& file) {
throw std::runtime_error("unsupported audio stream format");
}
Stream* audio::open_stream(const fs::path& file, bool keepSource) {
std::unique_ptr<Stream> audio::open_stream(const fs::path& file, bool keepSource) {
if (!keepSource && backend->isDummy()) {
auto header = load_PCM(file, true);
// using void source sized as audio instead of actual audio file
@@ -206,7 +203,7 @@ Stream* audio::open_stream(const fs::path& file, bool keepSource) {
);
}
Stream* audio::open_stream(std::shared_ptr<PCMStream> stream, bool keepSource) {
std::unique_ptr<Stream> audio::open_stream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return backend->openStream(stream, keepSource);
}
@@ -258,16 +255,17 @@ speakerid_t audio::play(
sound = sound->variants.at(index).get();
}
}
Speaker* speaker = sound->newInstance(priority, channel);
if (speaker == nullptr) {
auto speaker_ptr = sound->newInstance(priority, channel);
if (speaker_ptr == nullptr) {
remove_lower_priority_speaker(priority);
speaker = sound->newInstance(priority, channel);
speaker_ptr = sound->newInstance(priority, channel);
}
if (speaker == nullptr) {
if (speaker_ptr == nullptr) {
return 0;
}
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++;
speakers.emplace(id, speaker);
speakers.emplace(id, std::move(speaker_ptr));
speaker->setPosition(position);
speaker->setVolume(volume);
speaker->setPitch(pitch);
@@ -286,17 +284,18 @@ speakerid_t audio::play(
bool loop,
int channel
) {
Speaker* speaker = stream->createSpeaker(loop, channel);
if (speaker == nullptr) {
auto speaker_ptr = stream->createSpeaker(loop, channel);
if (speaker_ptr == nullptr) {
remove_lower_priority_speaker(PRIORITY_HIGH);
speaker = stream->createSpeaker(loop, channel);
speaker_ptr = stream->createSpeaker(loop, channel);
}
if (speaker == nullptr) {
if (speaker_ptr == nullptr) {
return 0;
}
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++;
streams.emplace(id, stream);
speakers.emplace(id, speaker);
speakers.emplace(id, std::move(speaker_ptr));
stream->bindSpeaker(id);
speaker->setPosition(position);
@@ -383,13 +382,12 @@ void audio::update(double delta) {
entry.second->update(delta);
}
float masterVolume = channels.at(0)->getVolume();
for (auto it = speakers.begin(); it != speakers.end();) {
auto speaker = it->second.get();
int speakerChannel = speaker->getChannel();
auto channel = get_channel(speakerChannel);
if (channel != nullptr) {
speaker->update(channel, speakerChannel == 0 ? 1.0f : masterVolume);
speaker->update(channel);
}
if (speaker->isStopped()) {
streams.erase(it->first);
+32 -32
View File
@@ -1,11 +1,12 @@
#ifndef AUDIO_AUDIO_H_
#define AUDIO_AUDIO_H_
#ifndef AUDIO_AUDIO_HPP_
#define AUDIO_AUDIO_HPP_
#include "../typedefs.hpp"
#include <vector>
#include <memory>
#include <filesystem>
#include <glm/glm.hpp>
#include "../typedefs.h"
namespace fs = std::filesystem;
@@ -178,7 +179,7 @@ namespace audio {
/// @param loop is stream looped (required for correct buffers preload)
/// @param channel channel index
/// @return speaker id or 0
virtual Speaker* createSpeaker(bool loop, int channel) = 0;
virtual std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) = 0;
/// @brief Unbind previous speaker and bind new speaker to the stream
/// @param speaker speaker id or 0 if all you need is unbind speaker
@@ -224,7 +225,7 @@ namespace audio {
/// @param channel channel index
/// @return new speaker with sound bound or nullptr
/// if all speakers are in use
virtual Speaker* newInstance(int priority, int channel) const = 0;
virtual std::unique_ptr<Speaker> newInstance(int priority, int channel) const = 0;
};
/// @brief Audio source controller interface.
@@ -235,8 +236,7 @@ namespace audio {
/// @brief Synchronize the speaker with channel settings
/// @param channel speaker channel
/// @param masterVolume volume of the master channel
virtual void update(const Channel* channel, float masterVolume) = 0;
virtual void update(const Channel* channel) = 0;
/// @brief Check speaker channel index
virtual int getChannel() const = 0;
@@ -336,8 +336,8 @@ namespace audio {
public:
virtual ~Backend() {};
virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0;
virtual std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0;
virtual void setListener(
glm::vec3 position,
glm::vec3 velocity,
@@ -353,52 +353,52 @@ namespace audio {
/// @brief Initialize audio system or use no audio mode
/// @param enabled try to initialize actual audio
extern void initialize(bool enabled);
void initialize(bool enabled);
/// @brief Load audio file info and PCM data
/// @param file audio file
/// @param headerOnly read header only
/// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return PCM audio data
extern PCM* load_PCM(const fs::path& file, bool headerOnly);
std::unique_ptr<PCM> load_PCM(const fs::path& file, bool headerOnly);
/// @brief Load sound from file
/// @param file audio file path
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
/// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return new Sound instance
extern Sound* load_sound(const fs::path& file, bool keepPCM);
std::unique_ptr<Sound> load_sound(const fs::path& file, bool keepPCM);
/// @brief Create new sound from PCM data
/// @param pcm PCM data
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
/// @return new Sound instance
extern Sound* create_sound(std::shared_ptr<PCM> pcm, bool keepPCM);
std::unique_ptr<Sound> create_sound(std::shared_ptr<PCM> pcm, bool keepPCM);
/// @brief Open new PCM stream from file
/// @param file audio file path
/// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return new PCMStream instance
extern PCMStream* open_PCM_stream(const fs::path& file);
std::unique_ptr<PCMStream> open_PCM_stream(const fs::path& file);
/// @brief Open new audio stream from file
/// @param file audio file path
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance
extern Stream* open_stream(const fs::path& file, bool keepSource);
std::unique_ptr<Stream> open_stream(const fs::path& file, bool keepSource);
/// @brief Open new audio stream from source
/// @param stream PCM data source
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance
extern Stream* open_stream(std::shared_ptr<PCMStream> stream, bool keepSource);
std::unique_ptr<Stream> open_stream(std::shared_ptr<PCMStream> stream, bool keepSource);
/// @brief Configure 3D listener
/// @param position listener position
/// @param velocity listener velocity (used for Doppler effect)
/// @param lookAt point the listener look at
/// @param up camera up vector
extern void set_listener(
void set_listener(
glm::vec3 position,
glm::vec3 velocity,
glm::vec3 lookAt,
@@ -416,7 +416,7 @@ namespace audio {
/// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH)
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play(
speakerid_t play(
Sound* sound,
glm::vec3 position,
bool relative,
@@ -436,7 +436,7 @@ namespace audio {
/// @param loop loop stream
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play(
speakerid_t play(
std::shared_ptr<Stream> stream,
glm::vec3 position,
bool relative,
@@ -455,7 +455,7 @@ namespace audio {
/// @param loop loop stream
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play_stream(
speakerid_t play_stream(
const fs::path& file,
glm::vec3 position,
bool relative,
@@ -468,49 +468,49 @@ namespace audio {
/// @brief Get speaker by id
/// @param id speaker id
/// @return speaker or nullptr
extern Speaker* get_speaker(speakerid_t id);
Speaker* get_speaker(speakerid_t id);
/// @brief Create new channel.
/// All non-builtin channels will be destroyed on audio::reset() call
/// @param name channel name
/// @return new channel index
extern int create_channel(const std::string& name);
int create_channel(const std::string& name);
/// @brief Get channel index by name
/// @param name channel name
/// @return channel index or -1
extern int get_channel_index(const std::string& name);
int get_channel_index(const std::string& name);
/// @brief Get channel by index. 0 - is master channel
/// @param index channel index
/// @return channel or nullptr
extern Channel* get_channel(int index);
Channel* get_channel(int index);
/// @brief Get channel by name.
/// @param name channel name
/// @return channel or nullptr
extern Channel* get_channel(const std::string& name);
Channel* get_channel(const std::string& name);
/// @brief Get stream associated with speaker
/// @param id speaker id
/// @return stream or nullptr
extern std::shared_ptr<Stream> get_associated_stream(speakerid_t id);
std::shared_ptr<Stream> get_associated_stream(speakerid_t id);
/// @brief Get alive speakers number (including paused)
extern size_t count_speakers();
size_t count_speakers();
/// @brief Get playing streams number (including paused)
extern size_t count_streams();
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);
void update(double delta);
/// @brief Stop all playing audio in channel, reset channel state
extern void reset_channel(int channel);
void reset_channel(int channel);
/// @brief Finalize audio system
extern void close();
void close();
};
#endif // AUDIO_AUDIO_H_
#endif // AUDIO_AUDIO_HPP_
+8 -6
View File
@@ -1,11 +1,13 @@
#include "GLSLExtension.h"
#include "GLSLExtension.hpp"
#include "../util/stringutil.hpp"
#include "../typedefs.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include "../util/stringutil.h"
#include "../typedefs.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
namespace fs = std::filesystem;
@@ -81,7 +83,7 @@ inline void source_line(std::stringstream& ss, uint linenum) {
ss << "#line " << linenum << "\n";
}
const std::string GLSLExtension::process(const fs::path file, const std::string& source) {
const std::string GLSLExtension::process(const fs::path& file, const std::string& source) {
std::stringstream ss;
size_t pos = 0;
uint linenum = 1;
@@ -1,5 +1,5 @@
#ifndef CODERS_GLSL_EXTESION_H_
#define CODERS_GLSL_EXTESION_H_
#ifndef CODERS_GLSL_EXTESION_HPP_
#define CODERS_GLSL_EXTESION_HPP_
#include <string>
#include <vector>
@@ -29,7 +29,10 @@ public:
bool hasHeader(const std::string& name) const;
bool hasDefine(const std::string& name) const;
const std::string process(const std::filesystem::path file, const std::string& source);
const std::string process(
const std::filesystem::path& file,
const std::string& source
);
};
#endif // CODERS_GLSL_EXTESION_H_
#endif // CODERS_GLSL_EXTESION_HPP_
+49 -69
View File
@@ -1,29 +1,32 @@
#include "binary_json.h"
#include "binary_json.hpp"
#include "gzip.hpp"
#include "byte_utils.hpp"
#include "../data/dynamic.hpp"
#include <stdexcept>
#include "gzip.h"
#include "byte_utils.h"
using namespace json;
using namespace dynamic;
static void to_binary(ByteBuilder& builder, const Value* value) {
switch (value->type) {
case valtype::map: {
std::vector<ubyte> bytes = to_binary(value->value.map);
static void to_binary(ByteBuilder& builder, const Value& value) {
switch (static_cast<Type>(value.index())) {
case Type::none:
throw std::runtime_error("none value is not implemented");
case Type::map: {
auto bytes = to_binary(std::get<Map_sptr>(value).get());
builder.put(bytes.data(), bytes.size());
break;
}
case valtype::list:
case Type::list:
builder.put(BJSON_TYPE_LIST);
for (auto& element : value->value.list->values) {
to_binary(builder, element.get());
for (auto& element : std::get<List_sptr>(value)->values) {
to_binary(builder, element);
}
builder.put(BJSON_END);
break;
case valtype::integer: {
int64_t val = value->value.integer;
case Type::integer: {
auto val = std::get<integer_t>(value);
if (val >= 0 && val <= 255) {
builder.put(BJSON_TYPE_BYTE);
builder.put(val);
@@ -39,22 +42,22 @@ static void to_binary(ByteBuilder& builder, const Value* value) {
}
break;
}
case valtype::number:
case Type::number:
builder.put(BJSON_TYPE_NUMBER);
builder.putFloat64(value->value.decimal);
builder.putFloat64(std::get<number_t>(value));
break;
case valtype::boolean:
builder.put(BJSON_TYPE_FALSE + value->value.boolean);
case Type::boolean:
builder.put(BJSON_TYPE_FALSE + std::get<bool>(value));
break;
case valtype::string:
case Type::string:
builder.put(BJSON_TYPE_STRING);
builder.put(*value->value.str);
builder.put(std::get<std::string>(value));
break;
}
}
static List* array_from_binary(ByteReader& reader);
static Map* object_from_binary(ByteReader& reader);
static std::unique_ptr<List> array_from_binary(ByteReader& reader);
static std::unique_ptr<Map> object_from_binary(ByteReader& reader);
std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
if (compress) {
@@ -70,7 +73,7 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
// writing entries
for (auto& entry : obj->values) {
builder.putCStr(entry.first.c_str());
to_binary(builder, entry.second.get());
to_binary(builder, entry.second);
}
// terminating byte
builder.put(BJSON_END);
@@ -80,79 +83,56 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
return builder.build();
}
static Value* value_from_binary(ByteReader& reader) {
static Value value_from_binary(ByteReader& reader) {
ubyte typecode = reader.get();
valtype type;
valvalue val;
switch (typecode) {
case BJSON_TYPE_DOCUMENT:
type = valtype::map;
reader.getInt32();
val.map = object_from_binary(reader);
break;
return Map_sptr(object_from_binary(reader).release());
case BJSON_TYPE_LIST:
type = valtype::list;
val.list = array_from_binary(reader);
break;
return List_sptr(array_from_binary(reader).release());
case BJSON_TYPE_BYTE:
type = valtype::integer;
val.integer = reader.get();
break;
return static_cast<integer_t>(reader.get());
case BJSON_TYPE_INT16:
type = valtype::integer;
val.integer = reader.getInt16();
break;
return static_cast<integer_t>(reader.getInt16());
case BJSON_TYPE_INT32:
type = valtype::integer;
val.integer = reader.getInt32();
break;
return static_cast<integer_t>(reader.getInt32());
case BJSON_TYPE_INT64:
type = valtype::integer;
val.integer = reader.getInt64();
break;
return reader.getInt64();
case BJSON_TYPE_NUMBER:
type = valtype::number;
val.decimal = reader.getFloat64();
break;
return reader.getFloat64();
case BJSON_TYPE_FALSE:
case BJSON_TYPE_TRUE:
type = valtype::boolean;
val.boolean = typecode - BJSON_TYPE_FALSE;
break;
return (typecode - BJSON_TYPE_FALSE) != 0;
case BJSON_TYPE_STRING:
type = valtype::string;
val.str = new std::string(reader.getString());
break;
return reader.getString();
default:
throw std::runtime_error(
"type "+std::to_string(typecode)+" is not supported");
"type "+std::to_string(typecode)+" is not supported"
);
}
return new Value(type, val);
}
static List* array_from_binary(ByteReader& reader) {
static std::unique_ptr<List> array_from_binary(ByteReader& reader) {
auto array = std::make_unique<List>();
auto& items = array->values;
while (reader.peek() != BJSON_END) {
items.push_back(std::unique_ptr<Value>(value_from_binary(reader)));
array->put(value_from_binary(reader));
}
reader.get();
return array.release();
return array;
}
static Map* object_from_binary(ByteReader& reader) {
static std::unique_ptr<Map> object_from_binary(ByteReader& reader) {
auto obj = std::make_unique<Map>();
auto& map = obj->values;
while (reader.peek() != BJSON_END) {
const char* key = reader.getCString();
Value* value = value_from_binary(reader);
map.insert(std::make_pair(key, value));
obj->put(key, value_from_binary(reader));
}
reader.get();
return obj.release();
return obj;
}
std::unique_ptr<Map> json::from_binary(const ubyte* src, size_t size) {
std::shared_ptr<Map> json::from_binary(const ubyte* src, size_t size) {
if (size < 2) {
throw std::runtime_error("bytes length is less than 2");
}
@@ -162,12 +142,12 @@ std::unique_ptr<Map> json::from_binary(const ubyte* src, size_t size) {
return from_binary(data.data(), data.size());
} else {
ByteReader reader(src, size);
std::unique_ptr<Value> value (value_from_binary(reader));
if (value->type != valtype::map) {
Value value = value_from_binary(reader);
if (auto map = std::get_if<Map_sptr>(&value)) {
return *map;
} else {
throw std::runtime_error("root value is not an object");
}
std::unique_ptr<Map> obj (value->value.map);
value->value.map = nullptr;
return obj;
}
}
@@ -1,9 +1,14 @@
#ifndef CODERS_BINARY_JSON_H_
#define CODERS_BINARY_JSON_H_
#ifndef CODERS_BINARY_JSON_HPP_
#define CODERS_BINARY_JSON_HPP_
#include "../typedefs.hpp"
#include <vector>
#include <memory>
#include "../data/dynamic.h"
namespace dynamic {
class Map;
}
namespace json {
const int BJSON_END = 0x0;
@@ -22,7 +27,7 @@ namespace json {
const int BJSON_TYPE_CDOCUMENT = 0x1F;
extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
extern std::unique_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
extern std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
}
#endif // CODERS_BINARY_JSON_H_
#endif // CODERS_BINARY_JSON_HPP_
+1 -1
View File
@@ -1,4 +1,4 @@
#include "byte_utils.h"
#include "byte_utils.hpp"
#include <cstring>
#include <limits>
@@ -1,9 +1,10 @@
#ifndef CODERS_BYTE_UTILS_H_
#define CODERS_BYTE_UTILS_H_
#ifndef CODERS_BYTE_UTILS_HPP_
#define CODERS_BYTE_UTILS_HPP_
#include "../typedefs.hpp"
#include <string>
#include <vector>
#include "../typedefs.h"
/* byteorder: little-endian */
class ByteBuilder {
@@ -44,7 +45,7 @@ public:
std::vector<ubyte> build();
};
/* byteorder: little-endian */
/// byteorder: little-endian
class ByteReader {
const ubyte* data;
size_t size;
@@ -54,26 +55,29 @@ public:
ByteReader(const ubyte* data);
void checkMagic(const char* data, size_t size);
/* Read one byte (unsigned 8 bit integer) */
/// @brief Read one byte (unsigned 8 bit integer)
ubyte get();
/* Read one byte (unsigned 8 bit integer) without pointer move */
/// @brief Read one byte (unsigned 8 bit integer) without pointer move
ubyte peek();
/* Read signed 16 bit integer */
/// @brief Read signed 16 bit integer
int16_t getInt16();
/* Read signed 32 bit integer */
/// @brief Read signed 32 bit integer
int32_t getInt32();
/* Read signed 64 bit integer */
/// @brief Read signed 64 bit integer
int64_t getInt64();
/* Read 32 bit floating-point number */
/// @brief Read 32 bit floating-point number
float getFloat32();
/* Read 64 bit floating-point number */
/// @brief Read 64 bit floating-point number
double getFloat64();
/// @brief Read C-String
const char* getCString();
/// @brief Read string with unsigned 32 bit number before (length)
std::string getString();
/// @return true if there is at least one byte remains
bool hasNext() const;
const ubyte* pointer() const;
void skip(size_t n);
};
#endif // CODERS_BYTE_UTILS_H_
#endif // CODERS_BYTE_UTILS_HPP_
+337 -344
View File
@@ -1,344 +1,337 @@
#include "commons.h"
#include <sstream>
#include <stdexcept>
#include <math.h>
inline double power(double base, int64_t power) {
double result = 1.0;
for (int64_t i = 0; i < power; i++) {
result *= base;
}
return result;
}
parsing_error::parsing_error(
std::string message,
std::string filename,
std::string source,
uint pos,
uint line,
uint linestart)
: std::runtime_error(message), filename(filename), source(source),
pos(pos), line(line), linestart(linestart) {
}
std::string parsing_error::errorLog() const {
std::stringstream ss;
uint linepos = pos - linestart;
ss << "parsing error in file '" << filename;
ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n";
size_t end = source.find("\n", linestart);
if (end == std::string::npos) {
end = source.length();
}
ss << source.substr(linestart, end-linestart) << "\n";
for (uint i = 0; i < linepos; i++) {
ss << " ";
}
ss << "^";
return ss.str();
}
std::string escape_string(std::string s) {
std::stringstream ss;
ss << '"';
for (char c : s) {
switch (c) {
case '\n': ss << "\\n"; break;
case '\r': ss << "\\r"; break;
case '\t': ss << "\\t"; break;
case '\f': ss << "\\f"; break;
case '\b': ss << "\\b"; break;
case '"': ss << "\\\""; break;
case '\\': ss << "\\\\"; break;
default:
if (c < ' ') {
ss << "\\" << std::oct << uint(ubyte(c));
break;
}
ss << c;
break;
}
}
ss << '"';
return ss.str();
}
BasicParser::BasicParser(std::string file, std::string source) : filename(file), source(source) {
}
void BasicParser::skipWhitespace() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
continue;
}
if (is_whitespace(next)) {
pos++;
} else {
break;
}
}
}
void BasicParser::skip(size_t n) {
n = std::min(n, source.length()-pos);
for (size_t i = 0; i < n; i++) {
char next = source[pos++];
if (next == '\n') {
line++;
linestart = pos;
}
}
}
void BasicParser::skipLine() {
while (hasNext()) {
if (source[pos] == '\n') {
pos++;
linestart = pos;
line++;
break;
}
pos++;
}
}
bool BasicParser::skipTo(const std::string& substring) {
size_t idx = source.find(substring, pos);
if (idx == std::string::npos) {
skip(source.length()-pos);
return false;
} else {
skip(idx-pos);
return true;
}
}
bool BasicParser::hasNext() {
return pos < source.length();
}
bool BasicParser::isNext(const std::string& substring) {
if (source.length() - pos < substring.length()) {
return false;
}
return source.substr(pos, substring.length()) == substring;
}
char BasicParser::nextChar() {
if (!hasNext()) {
throw error("unexpected end");
}
return source[pos++];
}
void BasicParser::expect(char expected) {
char c = peek();
if (c != expected) {
throw error("'"+std::string({expected})+"' expected");
}
pos++;
}
void BasicParser::expect(const std::string& substring) {
if (substring.empty())
return;
for (uint i = 0; i < substring.length(); i++) {
if (source.length() <= pos + i || source[pos+i] != substring[i]) {
throw error(escape_string(substring)+" expected");
}
}
pos += substring.length();
}
void BasicParser::expectNewLine() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
return;
}
if (is_whitespace(next)) {
pos++;
} else {
throw error("line separator expected");
}
}
}
void BasicParser::goBack() {
if (pos) pos--;
}
char BasicParser::peek() {
skipWhitespace();
if (pos >= source.length()) {
throw error("unexpected end");
}
return source[pos];
}
std::string BasicParser::parseName() {
char c = peek();
if (!is_identifier_start(c)) {
if (c == '"') {
pos++;
return parseString(c);
}
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_identifier_part(source[pos])) {
pos++;
}
return source.substr(start, pos-start);
}
int64_t BasicParser::parseSimpleInt(int base) {
char c = peek();
int index = hexchar2int(c);
if (index == -1 || index >= base) {
throw error("invalid number literal");
}
int64_t value = index;
pos++;
while (hasNext()) {
c = source[pos];
while (c == '_') {
c = source[++pos];
}
index = hexchar2int(c);
if (index == -1 || index >= base) {
return value;
}
value *= base;
value += index;
pos++;
}
return value;
}
bool BasicParser::parseNumber(int sign, number_u& out) {
char c = peek();
int base = 10;
if (c == '0' && pos + 1 < source.length() &&
(base = is_box(source[pos+1])) != 10) {
pos += 2;
out.ival = parseSimpleInt(base);
return true;
} else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') {
pos += 3;
out.fval = INFINITY * sign;
return false;
} else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') {
pos += 3;
out.fval = NAN * sign;
return false;
}
int64_t value = parseSimpleInt(base);
if (!hasNext()) {
out.ival = value * sign;
return true;
}
c = source[pos];
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
out.fval = sign * value * power(10.0, s * parseSimpleInt(10));
return false;
}
if (c == '.') {
pos++;
int64_t expo = 1;
while (hasNext() && source[pos] == '0') {
expo *= 10;
pos++;
}
int64_t afterdot = 0;
if (hasNext() && is_digit(source[pos])) {
afterdot = parseSimpleInt(10);
}
expo *= power(10, fmax(0, log10(afterdot) + 1));
c = source[pos];
double dvalue = (value + (afterdot / (double)expo));
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
out.fval = sign * dvalue * power(10.0, s * parseSimpleInt(10));
return false;
}
out.fval = sign * dvalue;
return false;
}
out.ival = sign * value;
return true;
}
std::string BasicParser::parseString(char quote, bool closeRequired) {
std::stringstream ss;
while (hasNext()) {
char c = source[pos];
if (c == quote) {
pos++;
return ss.str();
}
if (c == '\\') {
pos++;
c = nextChar();
if (c >= '0' && c <= '7') {
pos--;
ss << (char)parseSimpleInt(8);
continue;
}
switch (c) {
case 'n': ss << '\n'; break;
case 'r': ss << '\r'; break;
case 'b': ss << '\b'; break;
case 't': ss << '\t'; break;
case 'f': ss << '\f'; break;
case '\'': ss << '\\'; break;
case '"': ss << '"'; break;
case '\\': ss << '\\'; break;
case '/': ss << '/'; break;
case '\n': pos++; continue;
default:
throw error("'\\" + std::string({c}) +
"' is an illegal escape");
}
continue;
}
if (c == '\n' && closeRequired) {
throw error("non-closed string literal");
}
ss << c;
pos++;
}
if (closeRequired) {
throw error("unexpected end");
}
return ss.str();
}
parsing_error BasicParser::error(std::string message) {
return parsing_error(message, filename, source, pos, line, linestart);
}
#include "commons.hpp"
#include "../util/stringutil.hpp"
#include <sstream>
#include <stdexcept>
#include <math.h>
inline double power(double base, int64_t power) {
double result = 1.0;
for (int64_t i = 0; i < power; i++) {
result *= base;
}
return result;
}
parsing_error::parsing_error(
std::string message,
std::string_view filename,
std::string_view source,
uint pos,
uint line,
uint linestart
) : std::runtime_error(message), filename(filename),
pos(pos), line(line), linestart(linestart)
{
size_t end = source.find("\n", linestart);
if (end == std::string::npos) {
end = source.length();
}
this->source = source.substr(linestart, end-linestart);
}
std::string parsing_error::errorLog() const {
std::stringstream ss;
uint linepos = pos - linestart;
ss << "parsing error in file '" << filename;
ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n";
ss << source << "\n";
for (uint i = 0; i < linepos; i++) {
ss << " ";
}
ss << "^";
return ss.str();
}
BasicParser::BasicParser(
std::string_view file,
std::string_view source
) : filename(file), source(source) {
}
void BasicParser::skipWhitespace() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
continue;
}
if (is_whitespace(next)) {
pos++;
} else {
break;
}
}
}
void BasicParser::skip(size_t n) {
n = std::min(n, source.length()-pos);
for (size_t i = 0; i < n; i++) {
char next = source[pos++];
if (next == '\n') {
line++;
linestart = pos;
}
}
}
void BasicParser::skipLine() {
while (hasNext()) {
if (source[pos] == '\n') {
pos++;
linestart = pos;
line++;
break;
}
pos++;
}
}
bool BasicParser::skipTo(const std::string& substring) {
size_t idx = source.find(substring, pos);
if (idx == std::string::npos) {
skip(source.length()-pos);
return false;
} else {
skip(idx-pos);
return true;
}
}
bool BasicParser::hasNext() {
return pos < source.length();
}
bool BasicParser::isNext(const std::string& substring) {
if (source.length() - pos < substring.length()) {
return false;
}
return source.substr(pos, substring.length()) == substring;
}
char BasicParser::nextChar() {
if (!hasNext()) {
throw error("unexpected end");
}
return source[pos++];
}
void BasicParser::expect(char expected) {
char c = peek();
if (c != expected) {
throw error("'"+std::string({expected})+"' expected");
}
pos++;
}
void BasicParser::expect(const std::string& substring) {
if (substring.empty())
return;
for (uint i = 0; i < substring.length(); i++) {
if (source.length() <= pos + i || source[pos+i] != substring[i]) {
throw error(util::quote(substring)+" expected");
}
}
pos += substring.length();
}
void BasicParser::expectNewLine() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
return;
}
if (is_whitespace(next)) {
pos++;
} else {
throw error("line separator expected");
}
}
}
void BasicParser::goBack(size_t count) {
if (pos < count) {
throw std::runtime_error("pos < jump");
}
if (pos) {
pos -= count;
}
}
char BasicParser::peek() {
skipWhitespace();
if (pos >= source.length()) {
throw error("unexpected end");
}
return source[pos];
}
char BasicParser::peekNoJump() {
if (pos >= source.length()) {
throw error("unexpected end");
}
return source[pos];
}
std::string_view BasicParser::readUntil(char c) {
int start = pos;
while (hasNext() && source[pos] != c) {
pos++;
}
return source.substr(start, pos-start);
}
std::string BasicParser::parseName() {
char c = peek();
if (!is_identifier_start(c)) {
if (c == '"') {
pos++;
return parseString(c);
}
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos-start));
}
int64_t BasicParser::parseSimpleInt(int base) {
char c = peek();
int index = hexchar2int(c);
if (index == -1 || index >= base) {
throw error("invalid number literal");
}
int64_t value = index;
pos++;
while (hasNext()) {
c = source[pos];
while (c == '_') {
c = source[++pos];
}
index = hexchar2int(c);
if (index == -1 || index >= base) {
return value;
}
value *= base;
value += index;
pos++;
}
return value;
}
dynamic::Value BasicParser::parseNumber(int sign) {
char c = peek();
int base = 10;
if (c == '0' && pos + 1 < source.length() &&
(base = is_box(source[pos+1])) != 10) {
pos += 2;
return parseSimpleInt(base);
} else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') {
pos += 3;
return INFINITY * sign;
} else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') {
pos += 3;
return NAN * sign;
}
int64_t value = parseSimpleInt(base);
if (!hasNext()) {
return value * sign;
}
c = source[pos];
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
return sign * value * power(10.0, s * parseSimpleInt(10));
}
if (c == '.') {
pos++;
int64_t expo = 1;
while (hasNext() && source[pos] == '0') {
expo *= 10;
pos++;
}
int64_t afterdot = 0;
if (hasNext() && is_digit(source[pos])) {
afterdot = parseSimpleInt(10);
}
expo *= power(10, fmax(0, log10(afterdot) + 1));
c = source[pos];
double dvalue = (value + (afterdot / (double)expo));
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
return sign * dvalue * power(10.0, s * parseSimpleInt(10));
}
return sign * dvalue;
}
return sign * value;
}
std::string BasicParser::parseString(char quote, bool closeRequired) {
std::stringstream ss;
while (hasNext()) {
char c = source[pos];
if (c == quote) {
pos++;
return ss.str();
}
if (c == '\\') {
pos++;
c = nextChar();
if (c >= '0' && c <= '7') {
pos--;
ss << (char)parseSimpleInt(8);
continue;
}
switch (c) {
case 'n': ss << '\n'; break;
case 'r': ss << '\r'; break;
case 'b': ss << '\b'; break;
case 't': ss << '\t'; break;
case 'f': ss << '\f'; break;
case '\'': ss << '\\'; break;
case '"': ss << '"'; break;
case '\\': ss << '\\'; break;
case '/': ss << '/'; break;
case '\n': pos++; continue;
default:
throw error("'\\" + std::string({c}) +
"' is an illegal escape");
}
continue;
}
if (c == '\n' && closeRequired) {
throw error("non-closed string literal");
}
ss << c;
pos++;
}
if (closeRequired) {
throw error("unexpected end");
}
return ss.str();
}
parsing_error BasicParser::error(std::string message) {
return parsing_error(message, filename, source, pos, line, linestart);
}
+29 -29
View File
@@ -1,14 +1,11 @@
#ifndef CODERS_COMMONS_H_
#define CODERS_COMMONS_H_
#ifndef CODERS_COMMONS_HPP_
#define CODERS_COMMONS_HPP_
#include "../data/dynamic.hpp"
#include "../typedefs.hpp"
#include <string>
#include <stdexcept>
#include "../typedefs.h"
union number_u {
double fval;
int64_t ival;
};
inline int is_box(int c) {
switch (c) {
@@ -34,11 +31,11 @@ inline bool is_whitespace(int c) {
}
inline bool is_identifier_start(int c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '-' || c == '.';
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '.';
}
inline bool is_identifier_part(int c) {
return is_identifier_start(c) || is_digit(c);
return is_identifier_start(c) || is_digit(c) || c == '-';
}
inline int hexchar2int(int c) {
@@ -54,8 +51,6 @@ inline int hexchar2int(int c) {
return -1;
}
extern std::string escape_string(std::string s);
class parsing_error : public std::runtime_error {
public:
std::string filename;
@@ -64,20 +59,21 @@ public:
uint line;
uint linestart;
parsing_error(std::string message,
std::string filename,
std::string source,
uint pos,
uint line,
uint linestart);
parsing_error(
std::string message,
std::string_view filename,
std::string_view source,
uint pos,
uint line,
uint linestart
);
std::string errorLog() const;
};
class BasicParser {
protected:
std::string filename;
std::string source;
std::string_view filename;
std::string_view source;
uint pos = 0;
uint line = 1;
uint linestart = 0;
@@ -88,21 +84,25 @@ protected:
bool skipTo(const std::string& substring);
void expect(char expected);
void expect(const std::string& substring);
char peek();
char nextChar();
bool hasNext();
bool isNext(const std::string& substring);
void expectNewLine();
void goBack();
void goBack(size_t count=1);
std::string parseName();
int64_t parseSimpleInt(int base);
bool parseNumber(int sign, number_u& out);
dynamic::Value parseNumber(int sign);
std::string parseString(char chr, bool closeRequired=true);
parsing_error error(std::string message);
BasicParser(std::string filename, std::string source);
public:
std::string_view readUntil(char c);
std::string parseName();
bool hasNext();
char peek();
char peekNoJump();
char nextChar();
BasicParser(std::string_view file, std::string_view source);
};
#endif // CODERS_COMMONS_H_
#endif // CODERS_COMMONS_HPP_
+3 -2
View File
@@ -1,10 +1,11 @@
#include "gzip.h"
#include "gzip.hpp"
#include "byte_utils.hpp"
#define ZLIB_CONST
#include <zlib.h>
#include <math.h>
#include <memory>
#include "byte_utils.h"
std::vector<ubyte> gzip::compress(const ubyte* src, size_t size) {
size_t buffer_size = 23+size*1.01;
+4 -4
View File
@@ -1,8 +1,8 @@
#ifndef CODERS_GZIP_H_
#define CODERS_GZIP_H_
#ifndef CODERS_GZIP_HPP_
#define CODERS_GZIP_HPP_
#include "../typedefs.hpp"
#include <vector>
#include "../typedefs.h"
namespace gzip {
const unsigned char MAGIC[] = "\x1F\x8B";
@@ -18,4 +18,4 @@ namespace gzip {
std::vector<ubyte> decompress(const ubyte* src, size_t size);
}
#endif // CODERS_GZIP_H_
#endif // CODERS_GZIP_HPP_
+49
View File
@@ -0,0 +1,49 @@
#include "imageio.hpp"
#include "png.hpp"
#include "../graphics/core/ImageData.hpp"
#include <filesystem>
#include <functional>
#include <unordered_map>
namespace fs = std::filesystem;
using image_reader = std::function<std::unique_ptr<ImageData>(const std::string&)>;
using image_writer = std::function<void(const std::string&, const ImageData*)>;
static std::unordered_map<std::string, image_reader> readers {
{".png", png::load_image},
};
static std::unordered_map<std::string, image_writer> writers {
{".png", png::write_image},
};
bool imageio::is_read_supported(const std::string& extension) {
return readers.find(extension) != readers.end();
}
bool imageio::is_write_supported(const std::string& extension) {
return writers.find(extension) != writers.end();
}
inline std::string extensionOf(const std::string& filename) {
return fs::u8path(filename).extension().u8string();
}
std::unique_ptr<ImageData> imageio::read(const std::string& filename) {
auto found = readers.find(extensionOf(filename));
if (found == readers.end()) {
throw std::runtime_error("file format is not supported (read): "+filename);
}
return std::unique_ptr<ImageData>(found->second(filename));
}
void imageio::write(const std::string& filename, const ImageData* image) {
auto found = writers.find(extensionOf(filename));
if (found == writers.end()) {
throw std::runtime_error("file format is not supported (write): "+filename);
}
return found->second(filename, image);
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef CODERS_IMAGEIO_HPP_
#define CODERS_IMAGEIO_HPP_
#include <string>
#include <memory>
class ImageData;
namespace imageio {
inline const std::string PNG = ".png";
bool is_read_supported(const std::string& extension);
bool is_write_supported(const std::string& extension);
std::unique_ptr<ImageData> read(const std::string& filename);
void write(const std::string& filename, const ImageData* image);
}
#endif // CODERS_IMAGEIO_HPP_
+94 -96
View File
@@ -1,19 +1,31 @@
#include "json.h"
#include "json.hpp"
#include "../data/dynamic.hpp"
#include "../util/stringutil.hpp"
#include <math.h>
#include <sstream>
#include <iomanip>
#include <memory>
#include "commons.h"
#include "../data/dynamic.h"
using namespace json;
using namespace dynamic;
inline void newline(std::stringstream& ss,
bool nice, uint indent,
const std::string& indentstr) {
class Parser : BasicParser {
std::unique_ptr<dynamic::List> parseList();
std::unique_ptr<dynamic::Map> parseObject();
dynamic::Value parseValue();
public:
Parser(std::string_view filename, std::string_view source);
std::unique_ptr<dynamic::Map> parse();
};
inline void newline(
std::stringstream& ss,
bool nice, uint indent,
const std::string& indentstr
) {
if (nice) {
ss << "\n";
for (uint i = 0; i < indent; i++) {
@@ -24,39 +36,37 @@ inline void newline(std::stringstream& ss,
}
}
void stringify(const Value* value,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice);
void stringifyObj(
const Map* obj,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice
);
void stringifyObj(const Map* obj,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice);
void stringify(const Value* value,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice) {
if (value->type == valtype::map) {
stringifyObj(value->value.map, ss, indent, indentstr, nice);
void stringifyValue(
const Value& value,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice
) {
if (auto map = std::get_if<Map_sptr>(&value)) {
stringifyObj(map->get(), ss, indent, indentstr, nice);
}
else if (value->type == valtype::list) {
auto list = value->value.list;
else if (auto listptr = std::get_if<List_sptr>(&value)) {
auto list = *listptr;
if (list->size() == 0) {
ss << "[]";
return;
}
ss << '[';
for (uint i = 0; i < list->size(); i++) {
Value* value = list->get(i);
Value& value = list->get(i);
if (i > 0 || nice) {
newline(ss, nice, indent, indentstr);
}
stringify(value, ss, indent+1, indentstr, nice);
stringifyValue(value, ss, indent+1, indentstr, nice);
if (i + 1 < list->size()) {
ss << ',';
}
@@ -65,23 +75,26 @@ void stringify(const Value* value,
newline(ss, true, indent - 1, indentstr);
}
ss << ']';
} else if (value->type == valtype::boolean) {
ss << (value->value.boolean ? "true" : "false");
} else if (value->type == valtype::number) {
ss << std::setprecision(15);
ss << value->value.decimal;
} else if (value->type == valtype::integer) {
ss << value->value.integer;
} else if (value->type == valtype::string) {
ss << escape_string(*value->value.str);
} else if (auto flag = std::get_if<bool>(&value)) {
ss << (*flag ? "true" : "false");
} else if (auto num = std::get_if<number_t>(&value)) {
ss << std::setprecision(15) << *num;
} else if (auto num = std::get_if<integer_t>(&value)) {
ss << *num;
} else if (auto str = std::get_if<std::string>(&value)) {
ss << util::escape(*str);
} else {
ss << "null";
}
}
void stringifyObj(const Map* obj,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice) {
void stringifyObj(
const Map* obj,
std::stringstream& ss,
int indent,
const std::string& indentstr,
bool nice
) {
if (obj->values.empty()) {
ss << "{}";
return;
@@ -93,9 +106,9 @@ void stringifyObj(const Map* obj,
if (index > 0 || nice) {
newline(ss, nice, indent, indentstr);
}
Value* value = entry.second.get();
ss << escape_string(key) << ": ";
stringify(value, ss, indent+1, indentstr, nice);
const Value& value = entry.second;
ss << util::escape(key) << ": ";
stringifyValue(value, ss, indent+1, indentstr, nice);
index++;
if (index < obj->values.size()) {
ss << ',';
@@ -108,19 +121,30 @@ void stringifyObj(const Map* obj,
}
std::string json::stringify(
const Map* obj,
bool nice,
const std::string& indent) {
const Map* obj,
bool nice,
const std::string& indent
) {
std::stringstream ss;
stringifyObj(obj, ss, 1, indent, nice);
return ss.str();
}
Parser::Parser(std::string filename, std::string source)
: BasicParser(filename, source) {
std::string json::stringify(
const dynamic::Value& value,
bool nice,
const std::string& indent
) {
std::stringstream ss;
stringifyValue(value, ss, 1, indent, nice);
return ss.str();
}
Map* Parser::parse() {
Parser::Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
std::unique_ptr<Map> Parser::parse() {
char next = peek();
if (next != '{') {
throw error("'{' expected");
@@ -128,7 +152,7 @@ Map* Parser::parse() {
return parseObject();
}
Map* Parser::parseObject() {
std::unique_ptr<Map> Parser::parseObject() {
expect('{');
auto obj = std::make_unique<Map>();
auto& map = obj->values;
@@ -154,10 +178,10 @@ Map* Parser::parseObject() {
}
}
pos++;
return obj.release();
return obj;
}
List* Parser::parseList() {
std::unique_ptr<List> Parser::parseList() {
expect('[');
auto arr = std::make_unique<List>();
auto& values = arr->values;
@@ -166,7 +190,7 @@ List* Parser::parseList() {
skipLine();
continue;
}
values.push_back(std::unique_ptr<Value>(parseValue()));
values.push_back(parseValue());
char next = peek();
if (next == ',') {
@@ -178,75 +202,49 @@ List* Parser::parseList() {
}
}
pos++;
return arr.release();
return arr;
}
Value* Parser::parseValue() {
Value Parser::parseValue() {
char next = peek();
dynamic::valvalue val;
if (next == '-' || next == '+') {
pos++;
number_u num;
valtype type;
if (parseNumber(next == '-' ? -1 : 1, num)) {
val.integer = num.ival;
type = valtype::integer;
} else {
val.decimal = num.fval;
type = valtype::number;
}
return new Value(type, val);
return parseNumber(next == '-' ? -1 : 1);
}
if (is_identifier_start(next)) {
std::string literal = parseName();
if (literal == "true") {
val.boolean = true;
return new Value(valtype::boolean, val);
return true;
} else if (literal == "false") {
val.boolean = false;
return new Value(valtype::boolean, val);
return false;
} else if (literal == "inf") {
val.decimal = INFINITY;
return new Value(valtype::number, val);
return INFINITY;
} else if (literal == "nan") {
val.decimal = NAN;
return new Value(valtype::number, val);
return NAN;
}
throw error("invalid literal ");
}
if (next == '{') {
val.map = parseObject();
return new Value(valtype::map, val);
return Map_sptr(parseObject().release());
}
if (next == '[') {
val.list = parseList();
return new Value(valtype::list, val);
return List_sptr(parseList().release());
}
if (is_digit(next)) {
number_u num;
valtype type;
if (parseNumber(1, num)) {
val.integer = num.ival;
type = valtype::integer;
} else {
val.decimal = num.fval;
type = valtype::number;
}
return new Value(type, val);
return parseNumber(1);
}
if (next == '"' || next == '\'') {
pos++;
val.str = new std::string(parseString(next));
return new Value(valtype::string, val);
return parseString(next);
}
throw error("unexpected character '"+std::string({next})+"'");
}
std::unique_ptr<Map> json::parse(std::string filename, std::string source) {
std::unique_ptr<Map> json::parse(const std::string& filename, const std::string& source) {
Parser parser(filename, source);
return std::unique_ptr<Map>(parser.parse());
return parser.parse();
}
std::unique_ptr<Map> json::parse(std::string source) {
std::unique_ptr<Map> json::parse(const std::string& source) {
return parse("<string>", source);
}
-41
View File
@@ -1,41 +0,0 @@
#ifndef CODERS_JSON_H_
#define CODERS_JSON_H_
#include <vector>
#include <string>
#include <stdint.h>
#include <stdexcept>
#include <unordered_map>
#include "commons.h"
#include "../typedefs.h"
#include "binary_json.h"
namespace dynamic {
class Map;
class List;
class Value;
}
namespace json {
class Parser : public BasicParser {
dynamic::List* parseList();
dynamic::Map* parseObject();
dynamic::Value* parseValue();
public:
Parser(std::string filename, std::string source);
dynamic::Map* parse();
};
extern std::unique_ptr<dynamic::Map> parse(std::string filename, std::string source);
extern std::unique_ptr<dynamic::Map> parse(std::string source);
extern std::string stringify(
const dynamic::Map* obj,
bool nice,
const std::string& indent);
}
#endif // CODERS_JSON_H_
+33
View File
@@ -0,0 +1,33 @@
#ifndef CODERS_JSON_HPP_
#define CODERS_JSON_HPP_
#include "commons.hpp"
#include "binary_json.hpp"
#include "../data/dynamic.hpp"
#include "../typedefs.hpp"
#include <vector>
#include <string>
#include <stdint.h>
#include <stdexcept>
#include <unordered_map>
namespace json {
std::unique_ptr<dynamic::Map> parse(const std::string& filename, const std::string& source);
std::unique_ptr<dynamic::Map> parse(const std::string& source);
std::string stringify(
const dynamic::Map* obj,
bool nice,
const std::string& indent
);
std::string stringify(
const dynamic::Value& value,
bool nice,
const std::string& indent
);
}
#endif // CODERS_JSON_HPP_
+15 -10
View File
@@ -1,13 +1,16 @@
#include "ogg.h"
#include "ogg.hpp"
#include "../debug/Logger.hpp"
#include "../audio/audio.hpp"
#include "../typedefs.hpp"
#include <string>
#include <iostream>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include "../audio/audio.h"
#include "../typedefs.h"
static debug::Logger logger("ogg");
namespace fs = std::filesystem;
using namespace audio;
static inline std::string vorbis_error_message(int code) {
@@ -31,7 +34,7 @@ static inline std::string vorbis_error_message(int code) {
}
}
audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
std::unique_ptr<audio::PCM> ogg::load_pcm(const fs::path& file, bool headerOnly) {
OggVorbis_File vf;
int code;
if ((code = ov_fopen(file.u8string().c_str(), &vf))) {
@@ -56,7 +59,7 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
if (ret == 0) {
eof = true;
} else if (ret < 0) {
std::cerr << "ogg::load_pcm: " << vorbis_error_message(ret) << std::endl;
logger.error() << "ogg::load_pcm: " << vorbis_error_message(ret);
} else {
data.insert(data.end(), std::begin(buffer), std::begin(buffer)+ret);
}
@@ -64,7 +67,9 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
totalSamples = data.size() / channels / 2;
}
ov_clear(&vf);
return new PCM(std::move(data), totalSamples, channels, 16, sampleRate, seekable);
return std::make_unique<PCM>(
std::move(data), totalSamples, channels, 16, sampleRate, seekable
);
}
class OggStream : public PCMStream {
@@ -98,7 +103,7 @@ public:
int bitstream = 0;
long bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
if (bytes < 0) {
std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl;
logger.error() << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes;
return PCMStream::ERROR;
}
return bytes;
@@ -147,11 +152,11 @@ public:
}
};
PCMStream* ogg::create_stream(const std::filesystem::path& file) {
std::unique_ptr<PCMStream> ogg::create_stream(const fs::path& file) {
OggVorbis_File vf;
int code;
if ((code = ov_fopen(file.u8string().c_str(), &vf))) {
throw std::runtime_error("vorbis: "+vorbis_error_message(code));
}
return new OggStream(std::move(vf));
return std::make_unique<OggStream>(std::move(vf));
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef CODERS_OGG_H_
#define CODERS_OGG_H_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace ogg {
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
extern audio::PCMStream* create_stream(const std::filesystem::path& file);
}
#endif // CODERS_OGG_H_
+16
View File
@@ -0,0 +1,16 @@
#ifndef CODERS_OGG_HPP_
#define CODERS_OGG_HPP_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace ogg {
std::unique_ptr<audio::PCM> load_pcm(const std::filesystem::path& file, bool headerOnly);
std::unique_ptr<audio::PCMStream> create_stream(const std::filesystem::path& file);
}
#endif // CODERS_OGG_HPP_
+191 -204
View File
@@ -1,12 +1,14 @@
#include "png.h"
#include "png.hpp"
#include "../graphics/core/ImageData.hpp"
#include "../graphics/core/Texture.hpp"
#include "../files/files.hpp"
#include "../debug/Logger.hpp"
#include <iostream>
#include <memory>
#include <GL/glew.h>
#include "../graphics/ImageData.h"
#include "../graphics/Texture.h"
#include "../files/files.h"
static debug::Logger logger("png-coder");
#ifndef _WIN32
#define LIBPNG
@@ -19,78 +21,76 @@
int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) {
uint pixsize = alpha ? 4 : 3;
// Open file for writing (binary mode)
FILE* fp = fopen(filename, "wb");
if (fp == nullptr) {
fprintf(stderr, "Could not open file %s for writing\n", filename);
fclose(fp);
return 1;
}
// Open file for writing (binary mode)
FILE* fp = fopen(filename, "wb");
if (fp == nullptr) {
logger.error() << "could not open file " << filename << " for writing";
fclose(fp);
return 1;
}
// Initialize write structure
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
fprintf(stderr, "Could not allocate write struct\n");
fclose(fp);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 1;
}
// Initialize write structure
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
logger.error() << "could not allocate write struct";
fclose(fp);
return 1;
}
// Initialize info structure
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
fprintf(stderr, "Could not allocate info struct\n");
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 1;
}
// Initialize info structure
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
logger.error() << "could not allocate info struct";
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 1;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "Error during png creation\n");
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 1;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
logger.error() << "error during png creation";
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, &info_ptr);
return 1;
}
png_init_io(png_ptr, fp);
png_init_io(png_ptr, fp);
// Write header (8 bit colour depth)
png_set_IHDR(png_ptr, info_ptr, width, height,
8,
// Write header (8 bit colour depth)
png_set_IHDR(png_ptr, info_ptr, width, height,
8,
alpha ? PNG_COLOR_TYPE_RGBA :
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE,
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
png_write_info(png_ptr, info_ptr);
std::unique_ptr<png_byte[]> row(new png_byte[pixsize * width]);
// Write image data
for (uint y = 0; y < height; y++) {
for (uint x = 0; x < width; x++) {
auto row = std::make_unique<png_byte[]>(pixsize * width);
// Write image data
for (uint y = 0; y < height; y++) {
for (uint x = 0; x < width; x++) {
for (uint i = 0; i < pixsize; i++) {
row[x * pixsize + i] = (png_byte)data[(y * width + x) * pixsize + i];
}
}
png_write_row(png_ptr, row.get());
}
}
png_write_row(png_ptr, row.get());
}
// End write
png_write_end(png_ptr, nullptr);
// End write
png_write_end(png_ptr, nullptr);
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 0;
fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, &info_ptr);
return 0;
}
ImageData* _png_load(const char* file){
std::unique_ptr<ImageData> _png_load(const char* file){
FILE* fp = nullptr;
if ((fp = fopen(file, "rb")) == nullptr) {
return nullptr;
@@ -175,12 +175,12 @@ ImageData* _png_load(const char* file){
format = ImageFormat::rgb888;
break;
default:
printf("Color type %d not supported!\n", color_type);
logger.error() << "color type " << color_type << " is not supported!";
png_destroy_read_struct(&png, &info, &end_info);
fclose(fp);
fclose(fp);
return nullptr;
}
ImageData* image = new ImageData(format, width, height, (void*)image_data.release());
auto image = std::make_unique<ImageData>(format, width, height, (void*)image_data.release());
png_destroy_read_struct(&png, &info, &end_info);
fclose(fp);
return image;
@@ -194,172 +194,159 @@ ImageData* _png_load(const char* file){
static const int SPNG_SUCCESS = 0;
//returns spng result code
int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) {
int fmt;
int ret = 0;
spng_ctx* ctx = nullptr;
spng_ihdr ihdr = { 0 };
uint pixsize = alpha ? 4 : 3;
int fmt;
int ret = 0;
spng_ctx* ctx = nullptr;
spng_ihdr ihdr = { 0 };
uint pixsize = alpha ? 4 : 3;
ctx = spng_ctx_new(SPNG_CTX_ENCODER);
spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1);
ctx = spng_ctx_new(SPNG_CTX_ENCODER);
spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1);
ihdr.width = width;
ihdr.height = height;
ihdr.color_type = alpha ? SPNG_COLOR_TYPE_TRUECOLOR_ALPHA : SPNG_COLOR_TYPE_TRUECOLOR;
ihdr.bit_depth = 8;
ihdr.width = width;
ihdr.height = height;
ihdr.color_type = alpha ? SPNG_COLOR_TYPE_TRUECOLOR_ALPHA : SPNG_COLOR_TYPE_TRUECOLOR;
ihdr.bit_depth = 8;
spng_set_ihdr(ctx, &ihdr);
fmt = SPNG_FMT_PNG;
ret = spng_encode_image(ctx, data, (size_t)width * (size_t)height * pixsize , fmt, SPNG_ENCODE_FINALIZE);
if (ret != SPNG_SUCCESS) {
printf("spng_encode_image() error: %s\n", spng_strerror(ret));
fflush(stdout);
spng_ctx_free(ctx);
return ret;
}
spng_set_ihdr(ctx, &ihdr);
fmt = SPNG_FMT_PNG;
ret = spng_encode_image(ctx, data, (size_t)width * (size_t)height * pixsize , fmt, SPNG_ENCODE_FINALIZE);
if (ret != SPNG_SUCCESS) {
logger.error() << "spng_encode_image() error: " << spng_strerror(ret);
spng_ctx_free(ctx);
return ret;
}
size_t png_size;
void* png_buf = spng_get_png_buffer(ctx, &png_size, &ret);
size_t png_size;
void* png_buf = spng_get_png_buffer(ctx, &png_size, &ret);
if (png_buf == nullptr) {
printf("spng_get_png_buffer() error: %s\n", spng_strerror(ret));
}
else {
files::write_bytes(filename, (const unsigned char*)png_buf, png_size);
}
fflush(stdout);
spng_ctx_free(ctx);
return ret;
if (png_buf == nullptr) {
logger.error() << "spng_get_png_buffer() error: " << spng_strerror(ret);
}
else {
files::write_bytes(filename, (const unsigned char*)png_buf, png_size);
}
spng_ctx_free(ctx);
return ret;
}
ImageData* _png_load(const char* file){
int r = 0;
FILE *png = nullptr;
char *pngbuf = nullptr;
spng_ctx *ctx = nullptr;
unsigned char *out = nullptr;
std::unique_ptr<ImageData> _png_load(const char* file){
int r = 0;
FILE *png = nullptr;
char *pngbuf = nullptr;
spng_ctx *ctx = nullptr;
unsigned char *out = nullptr;
png = fopen(file, "rb");
if (png == nullptr){
std::cerr << "could not to open file " << file << std::endl;
return nullptr;
}
png = fopen(file, "rb");
if (png == nullptr){
logger.error() << "could not to open file " << file;
return nullptr;
}
fseek(png, 0, SEEK_END);
long siz_pngbuf = ftell(png);
rewind(png);
if(siz_pngbuf < 1) {
fclose(png);
std::cerr << "could not to read file " << file << std::endl;
return nullptr;
}
pngbuf = new char[siz_pngbuf];
if(fread(pngbuf, siz_pngbuf, 1, png) != 1){ //check of read elements count
fclose(png);
delete[] pngbuf;
std::cerr << "fread() failed" << std::endl;
return nullptr;
}
fclose(png); // <- finally closing file
ctx = spng_ctx_new(0);
if (ctx == nullptr){
delete[] pngbuf;
std::cerr << "spng_ctx_new() failed" << std::endl;
return nullptr;
}
r = spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
std::cerr << "spng_set_crc_action() error: " << spng_strerror(r) << std::endl;
return nullptr;
}
r = spng_set_png_buffer(ctx, pngbuf, siz_pngbuf);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
std::cerr << "spng_set_png_buffer() error: " << spng_strerror(r) << std::endl;
return nullptr;
}
fseek(png, 0, SEEK_END);
long siz_pngbuf = ftell(png);
rewind(png);
if(siz_pngbuf < 1) {
fclose(png);
logger.error() << "could not to read file " << file;
return nullptr;
}
pngbuf = new char[siz_pngbuf];
if(fread(pngbuf, siz_pngbuf, 1, png) != 1){ //check of read elements count
fclose(png);
delete[] pngbuf;
logger.error() << "fread() failed: " << file;
return nullptr;
}
fclose(png); // <- finally closing file
ctx = spng_ctx_new(0);
if (ctx == nullptr){
delete[] pngbuf;
logger.error() << "spng_ctx_new() failed";
return nullptr;
}
r = spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
logger.error() << "spng_set_crc_action(): " << spng_strerror(r);
return nullptr;
}
r = spng_set_png_buffer(ctx, pngbuf, siz_pngbuf);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
logger.error() << "spng_set_png_buffer(): " << spng_strerror(r);
return nullptr;
}
spng_ihdr ihdr;
r = spng_get_ihdr(ctx, &ihdr);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
std::cerr << "spng_get_ihdr() error: " << spng_strerror(r) << std::endl;
return nullptr;
}
//// Unused "something"
//const char *clr_type_str;
//if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)
// clr_type_str = "grayscale";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)
// clr_type_str = "truecolor";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
// clr_type_str = "indexed color";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
// clr_type_str = "grayscale with alpha";
//else
// clr_type_str = "truecolor with alpha";
spng_ihdr ihdr;
r = spng_get_ihdr(ctx, &ihdr);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
logger.error() << "spng_get_ihdr(): " << spng_strerror(r);
return nullptr;
}
size_t out_size;
r = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
std::cerr << "spng_decoded_image_size() error: " << spng_strerror(r) << std::endl;
return nullptr;
}
out = new unsigned char[out_size];
r = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0);
if (r != SPNG_SUCCESS){
delete[] out;
delete[] pngbuf;
spng_ctx_free(ctx);
std::cerr << "spng_decode_image() error: " << spng_strerror(r) << std::endl;
return nullptr;
}
size_t out_size;
r = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size);
if (r != SPNG_SUCCESS){
delete[] pngbuf;
spng_ctx_free(ctx);
logger.error() << "spng_decoded_image_size(): " << spng_strerror(r);
return nullptr;
}
out = new unsigned char[out_size];
r = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0);
if (r != SPNG_SUCCESS){
delete[] out;
delete[] pngbuf;
spng_ctx_free(ctx);
logger.error() << "spng_decode_image(): " << spng_strerror(r);
return nullptr;
}
unsigned char* flipped = new unsigned char[out_size];
unsigned char* flipped = new unsigned char[out_size];
for (size_t i = 0; i < ihdr.height; i+=1){
size_t rowsize = ihdr.width*4;
for (size_t j = 0; j < rowsize; j++){
flipped[(ihdr.height-i-1)*rowsize+j] = out[i*rowsize+j];
}
}
delete[] out; // <- finally delete out // no, delete spng usage
for (size_t i = 0; i < ihdr.height; i+=1){
size_t rowsize = ihdr.width*4;
for (size_t j = 0; j < rowsize; j++){
flipped[(ihdr.height-i-1)*rowsize+j] = out[i*rowsize+j];
}
}
delete[] out; // <- finally delete out // no, delete spng usage
ImageData* image = new ImageData(ImageFormat::rgba8888, ihdr.width, ihdr.height, (void*)flipped);
auto image = std::make_unique<ImageData>(ImageFormat::rgba8888, ihdr.width, ihdr.height, (void*)flipped);
delete[] pngbuf;
spng_ctx_free(ctx);
delete[] pngbuf;
spng_ctx_free(ctx);
return image;
}
#endif
ImageData* png::load_image(std::string filename) {
ImageData* image (_png_load(filename.c_str()));
if (image == nullptr) {
std::cerr << "Could not load image " << filename << std::endl;
return nullptr;
}
std::unique_ptr<ImageData> png::load_image(const std::string& filename) {
auto image = _png_load(filename.c_str());
if (image == nullptr) {
throw std::runtime_error("could not load image "+filename);
}
return image;
}
Texture* png::load_texture(std::string filename) {
std::unique_ptr<ImageData> image (_png_load(filename.c_str()));
if (image == nullptr){
std::cerr << "Could not load texture " << filename << std::endl;
return nullptr;
}
auto texture = Texture::from(image.get());
std::unique_ptr<Texture> png::load_texture(const std::string& filename) {
auto image = load_image(filename);
auto texture = Texture::from(image.get());
texture->setNearestFilter();
return texture;
}
void png::write_image(std::string filename, const ImageData* image) {
_png_write(filename.c_str(), image->getWidth(), image->getHeight(), (const ubyte*)image->getData(), image->getFormat() == ImageFormat::rgba8888);
}
void png::write_image(const std::string& filename, const ImageData* image) {
_png_write(
filename.c_str(),
image->getWidth(),
image->getHeight(),
(const ubyte*)image->getData(),
image->getFormat() == ImageFormat::rgba8888
);
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef CODERS_PNG_H_
#define CODERS_PNG_H_
#include <string>
#include "../typedefs.h"
class Texture;
class ImageData;
namespace png {
extern ImageData* load_image(std::string filename);
extern void write_image(std::string filename, const ImageData* image);
extern Texture* load_texture(std::string filename);
}
#endif /* CODERS_PNG_H_ */
+16
View File
@@ -0,0 +1,16 @@
#ifndef CODERS_PNG_HPP_
#define CODERS_PNG_HPP_
#include <memory>
#include <string>
class Texture;
class ImageData;
namespace png {
std::unique_ptr<ImageData> load_image(const std::string& filename);
void write_image(const std::string& filename, const ImageData* image);
std::unique_ptr<Texture> load_texture(const std::string& filename);
}
#endif // CODERS_PNG_HPP_
+90
View File
@@ -0,0 +1,90 @@
#include "rle.hpp"
size_t rle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
ubyte len = src[i++];
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t rle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
ubyte counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == 255) {
dst[offset++] = counter;
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
dst[offset++] = counter;
dst[offset++] = c;
return offset;
}
size_t extrle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
uint len = src[i++];
if (len & 0x80) {
len &= 0x7F;
len |= ((uint)src[i++]) << 7;
}
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t extrle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
uint counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == max_sequence) {
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
return offset;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef CODERS_RLE_HPP_
#define CODERS_RLE_HPP_
#include "../typedefs.hpp"
namespace rle {
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
namespace extrle {
constexpr uint max_sequence = 0x7FFF;
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
#endif // CODERS_RLE_HPP_
+133 -249
View File
@@ -1,249 +1,133 @@
#include "toml.h"
#include "commons.h"
#include <math.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <assert.h>
using std::string;
using namespace toml;
Section::Section(string name) : name(name) {
}
void Section::add(std::string name, Field field) {
if (fields.find(name) != fields.end()) {
throw std::runtime_error("field duplication");
}
fields[name] = field;
keyOrder.push_back(name);
}
void Section::add(string name, bool* ptr) {
add(name, {fieldtype::ftbool, ptr});
}
void Section::add(string name, int* ptr) {
add(name, {fieldtype::ftint, ptr});
}
void Section::add(string name, uint* ptr) {
add(name, {fieldtype::ftuint, ptr});
}
void Section::add(string name, float* ptr) {
add(name, {fieldtype::ftfloat, ptr});
}
void Section::add(string name, string* ptr) {
add(name, {fieldtype::ftstring, ptr});
}
string Section::getName() const {
return name;
}
const Field* Section::field(std::string name) const {
auto found = fields.find(name);
if (found == fields.end()) {
return nullptr;
}
return &found->second;
}
const std::vector<std::string>& Section::keys() const {
return keyOrder;
}
Wrapper::~Wrapper() {
for (auto entry : sections) {
delete entry.second;
}
}
Section& Wrapper::add(std::string name) {
if (sections.find(name) != sections.end()) {
throw std::runtime_error("section duplication");
}
Section* section = new Section(name);
sections[name] = section;
keyOrder.push_back(name);
return *section;
}
Section* Wrapper::section(std::string name) {
auto found = sections.find(name);
if (found == sections.end()) {
return nullptr;
}
return found->second;
}
std::string Wrapper::write() const {
std::stringstream ss;
for (string key : keyOrder) {
const Section* section = sections.at(key);
ss << "[" << key << "]\n";
for (const string& key : section->keys()) {
ss << key << " = ";
const Field* field = section->field(key);
assert(field != nullptr);
switch (field->type) {
case fieldtype::ftbool:
ss << (*((bool*)field->ptr) ? "true" : "false");
break;
case fieldtype::ftint: ss << *((int*)field->ptr); break;
case fieldtype::ftuint: ss << *((uint*)field->ptr); break;
case fieldtype::ftfloat: ss << *((float*)field->ptr); break;
case fieldtype::ftstring:
ss << escape_string(*((const string*)field->ptr));
break;
}
ss << "\n";
}
ss << "\n";
}
return ss.str();
}
Reader::Reader(Wrapper* wrapper, string file, string source) : BasicParser(file, source), wrapper(wrapper) {
}
void Reader::skipWhitespace() {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
void Reader::read() {
skipWhitespace();
if (!hasNext()) {
return;
}
readSection(nullptr);
}
inline bool is_numeric_type(fieldtype type) {
return type == fieldtype::ftint || type == fieldtype::ftfloat;
}
void Section::set(string name, double value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftbool: *(bool*)(field->ptr) = fabs(value) > 0.0; break;
case fieldtype::ftint: *(int*)(field->ptr) = value; break;
case fieldtype::ftuint: *(uint*)(field->ptr) = value; break;
case fieldtype::ftfloat: *(float*)(field->ptr) = value; break;
case fieldtype::ftstring: *(string*)(field->ptr) = std::to_string(value); break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Section::set(std::string name, bool value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftbool: *(bool*)(field->ptr) = value; break;
case fieldtype::ftint: *(int*)(field->ptr) = (int)value; break;
case fieldtype::ftuint: *(uint*)(field->ptr) = (uint)value; break;
case fieldtype::ftfloat: *(float*)(field->ptr) = (float)value; break;
case fieldtype::ftstring: *(string*)(field->ptr) = value ? "true" : "false"; break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Section::set(std::string name, std::string value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftstring: *(string*)(field->ptr) = value; break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Reader::readSection(Section* section /*nullable*/) {
while (hasNext()) {
skipWhitespace();
if (!hasNext()) {
break;
}
char c = nextChar();
if (c == '[') {
string name = parseName();
Section* section = wrapper->section(name);
pos++;
readSection(section);
return;
}
pos--;
string name = parseName();
expect('=');
c = peek();
if (is_digit(c)) {
number_u num;
if (parseNumber(1, num)) {
if (section)
section->set(name, (double)num.ival);
} else {
if (section)
section->set(name, num.fval);
}
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
number_u num;
if (parseNumber(sign, num)) {
if (section)
section->set(name, (double)num.ival);
} else {
if (section)
section->set(name, num.fval);
}
} else if (is_identifier_start(c)) {
string identifier = parseName();
if (identifier == "true" || identifier == "false") {
bool flag = identifier == "true";
if (section) {
section->set(name, flag);
}
} else if (identifier == "inf") {
if (section) {
section->set(name, INFINITY);
}
} else if (identifier == "nan") {
if (section) {
section->set(name, NAN);
}
}
} else if (c == '"' || c == '\'') {
pos++;
string str = parseString(c);
if (section) {
section->set(name, str);
}
} else {
throw error("feature is not supported");
}
expectNewLine();
}
}
#include "toml.hpp"
#include "commons.hpp"
#include "../data/setting.hpp"
#include "../data/dynamic.hpp"
#include "../util/stringutil.hpp"
#include "../files/settings_io.hpp"
#include <math.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <assert.h>
using namespace toml;
class Reader : BasicParser {
SettingsHandler& handler;
void skipWhitespace() override {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
void readSection(const std::string& section) {
while (hasNext()) {
skipWhitespace();
if (!hasNext()) {
break;
}
char c = nextChar();
if (c == '[') {
std::string name = parseName();
pos++;
readSection(name);
return;
}
pos--;
std::string name = section+"."+parseName();
expect('=');
c = peek();
if (is_digit(c)) {
auto num = parseNumber(1);
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
auto num = parseNumber(sign);
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (is_identifier_start(c)) {
std::string identifier = parseName();
if (handler.has(name)) {
if (identifier == "true" || identifier == "false") {
bool flag = identifier == "true";
handler.setValue(name, flag);
} else if (identifier == "inf") {
handler.setValue(name, INFINITY);
} else if (identifier == "nan") {
handler.setValue(name, NAN);
}
}
} else if (c == '"' || c == '\'') {
pos++;
std::string str = parseString(c);
if (handler.has(name)) {
handler.setValue(name, str);
}
} else {
throw error("feature is not supported");
}
expectNewLine();
}
}
public:
Reader(
SettingsHandler& handler,
std::string_view file,
std::string_view source)
: BasicParser(file, source), handler(handler) {
}
void read() {
skipWhitespace();
if (!hasNext()) {
return;
}
readSection("");
}
};
void toml::parse(
SettingsHandler& handler,
const std::string& file,
const std::string& source
) {
Reader reader(handler, file, source);
reader.read();
}
std::string toml::stringify(SettingsHandler& handler) {
auto& sections = handler.getSections();
std::stringstream ss;
for (auto& section : sections) {
ss << "[" << section.name << "]\n";
for (const std::string& key : section.keys) {
ss << key << " = ";
auto setting = handler.getSetting(section.name+"."+key);
assert(setting != nullptr);
if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
ss << integer->get();
} else if (auto number = dynamic_cast<NumberSetting*>(setting)) {
ss << number->get();
} else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
ss << (flag->get() ? "true" : "false");
} else if (auto string = dynamic_cast<StringSetting*>(setting)) {
ss << util::escape(string->get());
}
ss << "\n";
}
ss << "\n";
}
return ss.str();
}
-68
View File
@@ -1,68 +0,0 @@
#ifndef CODERS_TOML_H_
#define CODERS_TOML_H_
#include <string>
#include <vector>
#include <unordered_map>
#include "commons.h"
namespace toml {
enum class fieldtype {
ftbool,
ftint,
ftuint,
ftfloat,
ftstring,
};
struct Field {
fieldtype type;
void* ptr;
};
class Section {
std::unordered_map<std::string, Field> fields;
std::vector<std::string> keyOrder;
std::string name;
void add(std::string name, Field field);
public:
Section(std::string name);
void add(std::string name, bool* ptr);
void add(std::string name, int* ptr);
void add(std::string name, uint* ptr);
void add(std::string name, float* ptr);
void add(std::string name, std::string* ptr);
const Field* field(std::string name) const;
void set(std::string name, double value);
void set(std::string name, bool value);
void set(std::string name, std::string value);
std::string getName() const;
const std::vector<std::string>& keys() const;
};
class Wrapper {
std::unordered_map<std::string, Section*> sections;
std::vector<std::string> keyOrder;
public:
~Wrapper();
Section& add(std::string section);
Section* section(std::string name);
std::string write() const;
};
class Reader : public BasicParser {
Wrapper* wrapper;
void skipWhitespace() override;
void readSection(Section* section);
public:
Reader(Wrapper* wrapper, std::string file, std::string source);
void read();
};
}
#endif // CODERS_TOML_H_
+20
View File
@@ -0,0 +1,20 @@
#ifndef CODERS_TOML_HPP_
#define CODERS_TOML_HPP_
#include "commons.hpp"
#include <string>
class SettingsHandler;
namespace toml {
std::string stringify(SettingsHandler& handler);
void parse(
SettingsHandler& handler,
const std::string& file,
const std::string& source
);
}
#endif // CODERS_TOML_HPP_
+17 -10
View File
@@ -1,13 +1,17 @@
#include "wav.h"
#include "wav.hpp"
#include "../audio/audio.hpp"
#include "../debug/Logger.hpp"
#include <vector>
#include <string>
#include <cstring>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include "../audio/audio.h"
namespace fs = std::filesystem;
static debug::Logger logger("wav-coder");
bool is_big_endian() {
uint32_t ui32_v = 0x01020304;
@@ -65,7 +69,7 @@ public:
return 0;
}
if (in.fail()) {
std::cerr << "Wav::load_pcm: I/O error ocurred" << std::endl;
logger.error() << "Wav::load_pcm: I/O error ocurred";
return -1;
}
return in.gcount();
@@ -114,7 +118,7 @@ public:
}
};
audio::PCMStream* wav::create_stream(const std::filesystem::path& file) {
std::unique_ptr<audio::PCMStream> wav::create_stream(const fs::path& file) {
std::ifstream in(file, std::ios::binary);
if(!in.is_open()){
throw std::runtime_error("could not to open file '"+file.u8string()+"'");
@@ -197,7 +201,6 @@ audio::PCMStream* wav::create_stream(const std::filesystem::path& file) {
}
if(std::strncmp(buffer, "data", 4) != 0){
std::cerr << buffer << std::endl;
throw std::runtime_error("file is not a valid WAVE file (doesn't have 'data' tag)");
}
@@ -214,11 +217,13 @@ audio::PCMStream* wav::create_stream(const std::filesystem::path& file) {
if(in.fail()){
throw std::runtime_error("fail state set on the file");
}
return new WavStream(std::move(in), channels, bitsPerSample, sampleRate, size, initialOffset);
return std::make_unique<WavStream>(
std::move(in), channels, bitsPerSample, sampleRate, size, initialOffset
);
}
audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) {
std::unique_ptr<audio::PCMStream> stream(wav::create_stream(file));
std::unique_ptr<audio::PCM> wav::load_pcm(const fs::path& file, bool headerOnly) {
auto stream = wav::create_stream(file);
size_t totalSamples = stream->getTotalSamples();
uint channels = stream->getChannels();
@@ -233,5 +238,7 @@ audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) {
data.resize(size);
stream->readFully(data.data(), size, false);
}
return new audio::PCM(std::move(data), totalSamples, channels, bitsPerSample, sampleRate, true);
return std::make_unique<audio::PCM>(
std::move(data), totalSamples, channels, bitsPerSample, sampleRate, true
);
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef CODERS_WAV_H_
#define CODERS_WAV_H_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace wav {
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
extern audio::PCMStream* create_stream(const std::filesystem::path& file);
}
#endif // CODERS_WAV_H_
+16
View File
@@ -0,0 +1,16 @@
#ifndef CODERS_WAV_HPP_
#define CODERS_WAV_HPP_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace wav {
std::unique_ptr<audio::PCM> load_pcm(const std::filesystem::path& file, bool headerOnly);
std::unique_ptr<audio::PCMStream> create_stream(const std::filesystem::path& file);
}
#endif // CODERS_WAV_HPP_
+21 -15
View File
@@ -1,9 +1,10 @@
#include "xml.h"
#include "xml.hpp"
#include "../util/stringutil.hpp"
#include <charconv>
#include <stdexcept>
#include <sstream>
#include "../util/stringutil.h"
using namespace xml;
@@ -36,7 +37,7 @@ bool Attribute::asBool() const {
glm::vec2 Attribute::asVec2() const {
size_t pos = text.find(',');
if (pos == std::string::npos) {
throw std::runtime_error("invalid vec2 value "+escape_string(text));
return glm::vec2(util::parse_double(text, 0, text.length()));
}
return glm::vec2(
util::parse_double(text, 0, pos),
@@ -48,11 +49,11 @@ glm::vec2 Attribute::asVec2() const {
glm::vec3 Attribute::asVec3() const {
size_t pos1 = text.find(',');
if (pos1 == std::string::npos) {
throw std::runtime_error("invalid vec3 value "+escape_string(text));
return glm::vec3(util::parse_double(text, 0, text.length()));
}
size_t pos2 = text.find(',', pos1+1);
if (pos2 == std::string::npos) {
throw std::runtime_error("invalid vec3 value "+escape_string(text));
throw std::runtime_error("invalid vec3 value "+util::quote(text));
}
return glm::vec3(
util::parse_double(text, 0, pos1),
@@ -65,15 +66,15 @@ glm::vec3 Attribute::asVec3() const {
glm::vec4 Attribute::asVec4() const {
size_t pos1 = text.find(',');
if (pos1 == std::string::npos) {
throw std::runtime_error("invalid vec4 value "+escape_string(text));
return glm::vec4(util::parse_double(text, 0, text.length()));
}
size_t pos2 = text.find(',', pos1+1);
if (pos2 == std::string::npos) {
throw std::runtime_error("invalid vec4 value "+escape_string(text));
throw std::runtime_error("invalid vec4 value "+util::quote(text));
}
size_t pos3 = text.find(',', pos2+1);
if (pos3 == std::string::npos) {
throw std::runtime_error("invalid vec4 value "+escape_string(text));
throw std::runtime_error("invalid vec4 value "+util::quote(text));
}
return glm::vec4(
util::parse_double(text, 0, pos1),
@@ -99,7 +100,7 @@ glm::vec4 Attribute::asColor() const {
}
return glm::vec4(r / 255.f, g / 255.f, b / 255.f, a / 255.f);
} else {
throw std::runtime_error("hex colors are only supported");
return asVec4() / 255.f;
}
}
@@ -176,7 +177,7 @@ const std::string& Document::getEncoding() const {
return encoding;
}
Parser::Parser(std::string filename, std::string source)
Parser::Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
@@ -196,8 +197,13 @@ xmlelement Parser::parseOpenTag() {
if (peek() == '=') {
nextChar();
skipWhitespace();
expect('"');
attrtext = parseString('"');
char quote = peek();
if (quote != '\'' && quote != '"') {
throw error("string literal expected");
}
skip(1);
attrtext = parseString(quote);
}
node->set(attrname, attrtext);
}
@@ -244,7 +250,7 @@ std::string Parser::parseText() {
}
nextChar();
}
return source.substr(start, pos-start);
return std::string(source.substr(start, pos-start));
}
inline bool is_xml_identifier_start(char c) {
@@ -264,7 +270,7 @@ std::string Parser::parseXMLName() {
while (hasNext() && is_xml_identifier_part(source[pos])) {
pos++;
}
return source.substr(start, pos-start);
return std::string(source.substr(start, pos-start));
}
xmlelement Parser::parseElement() {
@@ -377,7 +383,7 @@ static void stringifyElement(
auto attr = entry.second;
ss << attr.getName();
if (!attr.getText().empty()) {
ss << "=" << escape_string(attr.getText());
ss << "=" << util::escape(attr.getText());
}
if (count + 1 < int(attrs.size())) {
ss << " ";
+7 -7
View File
@@ -1,5 +1,7 @@
#ifndef CODERS_XML_H_
#define CODERS_XML_H_
#ifndef CODERS_XML_HPP_
#define CODERS_XML_HPP_
#include "commons.hpp"
#include <string>
#include <memory>
@@ -7,8 +9,6 @@
#include <glm/glm.hpp>
#include <unordered_map>
#include "commons.h"
namespace xml {
class Node;
class Attribute;
@@ -108,7 +108,7 @@ namespace xml {
const std::string& getEncoding() const;
};
class Parser : public BasicParser {
class Parser : BasicParser {
xmldocument document;
xmlelement parseOpenTag();
@@ -118,7 +118,7 @@ namespace xml {
std::string parseText();
std::string parseXMLName();
public:
Parser(std::string filename, std::string source);
Parser(std::string_view filename, std::string_view source);
xmldocument parse();
};
@@ -141,4 +141,4 @@ namespace xml {
extern xmldocument parse(std::string filename, std::string source);
}
#endif // CODERS_XML_H_
#endif // CODERS_XML_HPP_
+20 -11
View File
@@ -1,17 +1,24 @@
#ifndef SRC_CONSTANTS_H_
#define SRC_CONSTANTS_H_
#ifndef CONSTANTS_HPP_
#define CONSTANTS_HPP_
#include "typedefs.hpp"
#include <limits>
#include <string>
#include "typedefs.h"
inline constexpr int ENGINE_VERSION_MAJOR = 0;
inline constexpr int ENGINE_VERSION_MINOR = 21;
inline constexpr bool ENGINE_VERSION_INDEV = true;
#ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false;
#else
inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG
inline const std::string ENGINE_VERSION_STRING = "0.21";
inline constexpr int BLOCK_AIR = 0;
inline constexpr int ITEM_EMPTY = 0;
inline constexpr blockid_t BLOCK_AIR = 0;
inline constexpr itemid_t ITEM_EMPTY = 0;
inline constexpr int CHUNK_W = 16;
inline constexpr int CHUNK_H = 256;
@@ -20,19 +27,21 @@ inline constexpr int CHUNK_D = 16;
inline constexpr uint VOXEL_USER_BITS = 8;
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
/// @brief pixel size of an item inventory icon
inline constexpr int ITEM_ICON_SIZE = 48;
/* Chunk volume (count of voxels per Chunk) */
/// @brief chunk volume (count of voxels per Chunk)
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
/* BLOCK_VOID is block id used to mark non-existing voxel (voxel of missing chunk) */
/// @brief block id used to mark non-existing voxel (voxel of missing chunk)
inline constexpr blockid_t BLOCK_VOID = std::numeric_limits<blockid_t>::max();
/// @brief item id used to mark non-existing item (error)
inline constexpr itemid_t ITEM_VOID = std::numeric_limits<itemid_t>::max();
/// @brief max number of block definitions possible
inline constexpr blockid_t MAX_BLOCKS = BLOCK_VOID;
inline constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=CHUNK_D) {
return (y * d + z) * w + x;
return (y * d + z) * w + x;
}
inline const std::string SHADERS_FOLDER = "shaders";
@@ -41,4 +50,4 @@ inline const std::string FONTS_FOLDER = "fonts";
inline const std::string LAYOUTS_FOLDER = "layouts";
inline const std::string SOUNDS_FOLDER = "sounds";
#endif // SRC_CONSTANTS_H_
#endif // CONSTANTS_HPP_
+14 -9
View File
@@ -1,14 +1,14 @@
#include "Content.h"
#include "Content.hpp"
#include <memory>
#include <stdexcept>
#include <glm/glm.hpp>
#include "../voxels/Block.h"
#include "../items/ItemDef.h"
#include "../voxels/Block.hpp"
#include "../items/ItemDef.hpp"
#include "ContentPack.h"
#include "../logic/scripting/scripting.h"
#include "ContentPack.hpp"
#include "../logic/scripting/scripting.hpp"
ContentBuilder::~ContentBuilder() {}
@@ -95,9 +95,7 @@ Content* ContentBuilder::build() {
}
blockDefsIndices.push_back(def);
if (groups->find(def->drawGroup) == groups->end()) {
groups->insert(def->drawGroup);
}
groups->insert(def->drawGroup);
}
std::vector<ItemDef*> itemDefsIndices;
@@ -155,7 +153,14 @@ Content::Content(
drawGroups(std::move(drawGroups))
{}
Content::~Content() {}
Content::~Content() {
for (auto& entry : blockDefs) {
delete entry.second;
}
for (auto& entry : itemDefs) {
delete entry.second;
}
}
Block* Content::findBlock(std::string id) const {
auto found = blockDefs.find(id);
@@ -1,5 +1,5 @@
#ifndef CONTENT_CONTENT_H_
#define CONTENT_CONTENT_H_
#ifndef CONTENT_CONTENT_HPP_
#define CONTENT_CONTENT_HPP_
#include <string>
#include <vector>
@@ -7,8 +7,8 @@
#include <stdexcept>
#include <unordered_map>
#include <set>
#include "../typedefs.h"
#include "../voxels/Block.h"
#include "../typedefs.hpp"
#include "../voxels/Block.hpp"
using DrawGroups = std::set<ubyte>;
@@ -146,4 +146,4 @@ public:
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const;
};
#endif // CONTENT_CONTENT_H_
#endif // CONTENT_CONTENT_HPP_
+15 -14
View File
@@ -1,16 +1,15 @@
#include "ContentLUT.h"
#include "ContentLUT.hpp"
#include "Content.hpp"
#include "../constants.hpp"
#include "../files/files.hpp"
#include "../coders/json.hpp"
#include "../voxels/Block.hpp"
#include "../items/ItemDef.hpp"
#include "../data/dynamic.hpp"
#include <memory>
#include "Content.h"
#include "../constants.h"
#include "../files/files.h"
#include "../coders/json.h"
#include "../voxels/Block.h"
#include "../items/ItemDef.h"
#include "../data/dynamic.h"
ContentLUT::ContentLUT(const Content* content, size_t blocksCount, size_t itemsCount) {
auto* indices = content->getIndices();
for (size_t i = 0; i < blocksCount; i++) {
@@ -34,8 +33,10 @@ ContentLUT::ContentLUT(const Content* content, size_t blocksCount, size_t itemsC
}
}
ContentLUT* ContentLUT::create(const fs::path& filename,
const Content* content) {
std::shared_ptr<ContentLUT> ContentLUT::create(
const fs::path& filename,
const Content* content
) {
auto root = files::read_json(filename);
auto blocklist = root->list("blocks");
auto itemlist = root->list("items");
@@ -48,7 +49,7 @@ ContentLUT* ContentLUT::create(const fs::path& filename,
? std::max(itemlist->size(), indices->countItemDefs())
: indices->countItemDefs();
auto lut = std::make_unique<ContentLUT>(content, blocks_c, items_c);
auto lut = std::make_shared<ContentLUT>(content, blocks_c, items_c);
if (blocklist) {
for (size_t i = 0; i < blocklist->size(); i++) {
@@ -75,7 +76,7 @@ ContentLUT* ContentLUT::create(const fs::path& filename,
}
if (lut->hasContentReorder() || lut->hasMissingContent()) {
return lut.release();
return lut;
} else {
return nullptr;
}
@@ -1,15 +1,15 @@
#ifndef CONTENT_CONTENT_LUT_H_
#define CONTENT_CONTENT_LUT_H_
#ifndef CONTENT_CONTENT_LUT_HPP_
#define CONTENT_CONTENT_LUT_HPP_
#include "Content.hpp"
#include "../typedefs.hpp"
#include "../constants.hpp"
#include <string>
#include <vector>
#include <filesystem>
#include "../typedefs.h"
#include "../constants.h"
#include "Content.h"
namespace fs = std::filesystem;
struct contententry {
@@ -70,7 +70,7 @@ public:
}
}
static ContentLUT* create(
static std::shared_ptr<ContentLUT> create(
const fs::path& filename,
const Content* content
);
@@ -93,4 +93,4 @@ public:
std::vector<contententry> getMissingContent() const;
};
#endif // CONTENT_CONTENT_LUT_H_
#endif // CONTENT_CONTENT_LUT_HPP_
+24 -23
View File
@@ -1,4 +1,17 @@
#include "ContentLoader.h"
#include "ContentLoader.hpp"
#include "Content.hpp"
#include "ContentPack.hpp"
#include "../coders/json.hpp"
#include "../core_defs.hpp"
#include "../data/dynamic.hpp"
#include "../debug/Logger.hpp"
#include "../files/files.hpp"
#include "../items/ItemDef.hpp"
#include "../logic/scripting/scripting.hpp"
#include "../typedefs.hpp"
#include "../util/listutil.hpp"
#include "../voxels/Block.hpp"
#include <iostream>
#include <string>
@@ -6,21 +19,10 @@
#include <algorithm>
#include <glm/glm.hpp>
#include "Content.h"
#include "../items/ItemDef.h"
#include "../util/listutil.h"
#include "../voxels/Block.h"
#include "../files/files.h"
#include "../coders/json.h"
#include "../typedefs.h"
#include "../core_defs.h"
#include "../data/dynamic.h"
#include "ContentPack.h"
#include "../logic/scripting/scripting.h"
namespace fs = std::filesystem;
static debug::Logger logger("content-loader");
ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) {
}
@@ -86,11 +88,11 @@ void ContentLoader::fixPackIndices() {
auto blocksFolder = folder/ContentPack::BLOCKS_FOLDER;
auto itemsFolder = folder/ContentPack::ITEMS_FOLDER;
std::unique_ptr<dynamic::Map> root;
dynamic::Map_sptr root;
if (fs::is_regular_file(indexFile)) {
root = files::read_json(indexFile);
} else {
root.reset(new dynamic::Map());
root = dynamic::create_map();
}
bool modified = false;
@@ -100,7 +102,6 @@ void ContentLoader::fixPackIndices() {
if (modified){
// rewrite modified json
std::cout << indexFile << std::endl;
files::write_json(indexFile, root.get());
}
}
@@ -227,15 +228,15 @@ void ContentLoader::loadCustomBlockModel(Block& def, dynamic::Map* primitives) {
def.modelBoxes.push_back(modelbox);
if (boxarr->size() == 7)
for (uint i = 6; i < 12; i++) {
for (uint j = 6; j < 12; j++) {
def.modelTextures.push_back(boxarr->str(6));
}
else if (boxarr->size() == 12)
for (uint i = 6; i < 12; i++) {
def.modelTextures.push_back(boxarr->str(i));
for (uint j = 6; j < 12; j++) {
def.modelTextures.push_back(boxarr->str(j));
}
else
for (uint i = 6; i < 12; i++) {
for (uint j = 6; j < 12; j++) {
def.modelTextures.push_back("notfound");
}
}
@@ -321,11 +322,11 @@ BlockMaterial ContentLoader::loadBlockMaterial(fs::path file, std::string full)
}
void ContentLoader::load(ContentBuilder& builder) {
std::cout << "-- loading pack [" << pack->id << "]" << std::endl;
logger.info() << "loading pack [" << pack->id << "]";
auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack));
builder.add(runtime);
env = runtime->getEnvironment()->getId();
env = runtime->getEnvironment();
ContentPackStats& stats = runtime->getStatsWriteable();
fixPackIndices();
@@ -1,7 +1,7 @@
#ifndef CONTENT_CONTENT_LOADER_H_
#define CONTENT_CONTENT_LOADER_H_
#ifndef CONTENT_CONTENT_LOADER_HPP_
#define CONTENT_CONTENT_LOADER_HPP_
#include "../voxels/Block.h"
#include "../voxels/Block.hpp"
#include <string>
#include <filesystem>
@@ -18,7 +18,7 @@ namespace dynamic {
class ContentLoader {
const ContentPack* pack;
int env = 0;
scriptenv env;
void loadBlock(Block& def, std::string full, std::string name);
void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
@@ -38,4 +38,4 @@ public:
void load(ContentBuilder& builder);
};
#endif // CONTENT_CONTENT_LOADER_H_
#endif // CONTENT_CONTENT_LOADER_HPP_
+12 -37
View File
@@ -1,13 +1,12 @@
#include "ContentPack.h"
#include "ContentPack.hpp"
#include <iostream>
#include <algorithm>
#include "../coders/json.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
#include "../data/dynamic.h"
#include "../logic/scripting/scripting.h"
#include "../coders/json.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include "../data/dynamic.hpp"
namespace fs = std::filesystem;
@@ -75,7 +74,9 @@ ContentPack ContentPack::read(fs::path folder) {
auto dependencies = root->list("dependencies");
if (dependencies) {
for (size_t i = 0; i < dependencies->size(); i++) {
pack.dependencies.push_back(dependencies->str(i));
pack.dependencies.push_back(
{DependencyLevel::required, dependencies->str(i)}
);
}
}
@@ -111,21 +112,6 @@ void ContentPack::scanFolder(
}
}
void ContentPack::scan(
fs::path rootfolder,
EnginePaths* paths,
std::vector<ContentPack>& packs
) {
scanFolder(paths->getResources()/fs::path("content"), packs);
scanFolder(paths->getUserfiles()/fs::path("content"), packs);
scanFolder(rootfolder, packs);
}
void ContentPack::scan(EnginePaths* paths,
std::vector<ContentPack>& packs) {
scan(paths->getWorldFolder()/fs::path("content"), paths, packs);
}
std::vector<std::string> ContentPack::worldPacksList(fs::path folder) {
fs::path listfile = folder / fs::path("packs.list");
if (!fs::is_regular_file(listfile)) {
@@ -152,23 +138,12 @@ fs::path ContentPack::findPack(const EnginePaths* paths, fs::path worldDir, std:
return folder;
}
void ContentPack::readPacks(const EnginePaths* paths,
std::vector<ContentPack>& packs,
const std::vector<std::string>& packnames,
fs::path worldDir) {
for (const auto& name : packnames) {
fs::path packfolder = ContentPack::findPack(paths, worldDir, name);
if (!fs::is_directory(packfolder)) {
throw contentpack_error(name, packfolder,
"could not to find pack '"+name+"'");
}
packs.push_back(ContentPack::read(packfolder));
}
}
ContentPackRuntime::ContentPackRuntime(
ContentPack info,
std::unique_ptr<scripting::Environment> env
scriptenv env
) : info(info), env(std::move(env))
{
}
ContentPackRuntime::~ContentPackRuntime() {
}
@@ -1,5 +1,7 @@
#ifndef CONTENT_CONTENT_PACK_H_
#define CONTENT_CONTENT_PACK_H_
#ifndef CONTENT_CONTENT_PACK_HPP_
#define CONTENT_CONTENT_PACK_HPP_
#include "../typedefs.hpp"
#include <string>
#include <vector>
@@ -26,6 +28,19 @@ public:
fs::path getFolder() const;
};
enum class DependencyLevel {
required, // dependency must be installed
optional, // dependency will be installed if found
weak, // dependency will not be installed automatically
};
/// @brief Content-pack that should be installed before the dependent
struct DependencyPack {
DependencyLevel level;
std::string id;
};
struct ContentPack {
std::string id = "none";
std::string title = "untitled";
@@ -33,7 +48,7 @@ struct ContentPack {
std::string creator = "";
std::string description = "no description";
fs::path folder;
std::vector<std::string> dependencies;
std::vector<DependencyPack> dependencies;
fs::path getContentFile() const;
@@ -50,16 +65,6 @@ struct ContentPack {
fs::path folder,
std::vector<ContentPack>& packs
);
static void scan(
fs::path folder,
EnginePaths* paths,
std::vector<ContentPack>& packs
);
static void scan(
EnginePaths* paths,
std::vector<ContentPack>& packs
);
static std::vector<std::string> worldPacksList(fs::path folder);
@@ -68,13 +73,6 @@ struct ContentPack {
fs::path worldDir,
std::string name
);
static void readPacks(
const EnginePaths* paths,
std::vector<ContentPack>& packs,
const std::vector<std::string>& names,
fs::path worldDir
);
};
struct ContentPackStats {
@@ -89,12 +87,13 @@ struct ContentPackStats {
class ContentPackRuntime {
ContentPack info;
ContentPackStats stats {};
std::unique_ptr<scripting::Environment> env;
scriptenv env;
public:
ContentPackRuntime(
ContentPack info,
std::unique_ptr<scripting::Environment> env
scriptenv env
);
~ContentPackRuntime();
inline const ContentPackStats& getStats() const {
return stats;
@@ -112,9 +111,9 @@ public:
return info;
}
inline scripting::Environment* getEnvironment() const {
return env.get();
inline scriptenv getEnvironment() const {
return env;
}
};
#endif // CONTENT_CONTENT_PACK_H_
#endif // CONTENT_CONTENT_PACK_HPP_
+158
View File
@@ -0,0 +1,158 @@
#include "PacksManager.hpp"
#include "../util/listutil.hpp"
#include <queue>
#include <sstream>
PacksManager::PacksManager() {
}
void PacksManager::setSources(std::vector<fs::path> sources) {
this->sources = sources;
}
void PacksManager::scan() {
packs.clear();
std::vector<ContentPack> packsList;
for (auto& folder : sources) {
ContentPack::scanFolder(folder, packsList);
for (auto& pack : packsList) {
packs.emplace(pack.id, pack);
}
}
}
void PacksManager::exclude(const std::string& id) {
packs.erase(id);
}
std::vector<std::string> PacksManager::getAllNames() const {
std::vector<std::string> names;
for (auto& entry : packs) {
names.push_back(entry.first);
}
return names;
}
std::vector<ContentPack> PacksManager::getAll(const std::vector<std::string>& names) const {
std::vector<ContentPack> packsList;
for (auto& name : names) {
auto found = packs.find(name);
if (found == packs.end()) {
throw contentpack_error(name, fs::path(""), "pack not found");
}
packsList.push_back(found->second);
}
return packsList;
}
static contentpack_error on_circular_dependency(std::queue<const ContentPack*>& queue) {
const ContentPack* lastPack = queue.back();
// circular dependency
std::stringstream ss;
ss << "circular dependency: " << lastPack->id;
while (!queue.empty()) {
auto* pack = queue.front();
queue.pop();
ss << " <- " << pack->id;
}
return contentpack_error(lastPack->id, lastPack->folder, ss.str());
}
/// @brief Resolve pack dependencies
/// @param pack current pack
/// @param packs all available packs repository
/// @param allNames all already done or enqueued packs
/// @param added packs with all dependencies resolved
/// @param queue current pass queue
/// @param resolveWeaks make weak dependencies resolved if found but not added to queue
/// @return true if all dependencies are already added or not found (optional/weak)
/// @throws contentpack_error if required dependency is not found
static bool resolve_dependencies (
const ContentPack* pack,
const std::unordered_map<std::string, ContentPack>& packs,
std::vector<std::string>& allNames,
const std::vector<std::string>& added,
std::queue<const ContentPack*>& queue,
bool resolveWeaks
) {
bool satisfied = true;
for (auto& dep : pack->dependencies) {
if (util::contains(added, dep.id)) {
continue;
}
auto found = packs.find(dep.id);
bool exists = found != packs.end();
if (!exists && dep.level == DependencyLevel::required) {
throw contentpack_error(dep.id, fs::path(), "dependency of '"+pack->id+"'");
}
if (!exists) {
// ignored for optional or weak dependencies
continue;
}
if (resolveWeaks && dep.level == DependencyLevel::weak) {
// dependency pack is found but not added yet
// resolveWeaks is used on second iteration, so it's will not be added
continue;
}
if (!util::contains(allNames, dep.id) && dep.level != DependencyLevel::weak) {
allNames.push_back(dep.id);
queue.push(&found->second);
}
satisfied = false;
}
return satisfied;
}
std::vector<std::string> PacksManager::assembly(const std::vector<std::string>& names) const {
std::vector<std::string> allNames = names;
std::vector<std::string> added;
std::queue<const ContentPack*> queue;
std::queue<const ContentPack*> queue2;
for (auto& name : names) {
auto found = packs.find(name);
if (found == packs.end()) {
throw contentpack_error(name, fs::path(""), "pack not found");
}
queue.push(&found->second);
}
bool resolveWeaks = false;
while (!queue.empty()) {
int addedInIteration = 0;
while (!queue.empty()) {
auto* pack = queue.front();
queue.pop();
if (resolve_dependencies(pack, packs, allNames, added, queue, resolveWeaks)) {
if (util::contains(added, pack->id)) {
throw contentpack_error(pack->id, pack->folder, "pack duplication");
}
added.push_back(pack->id);
addedInIteration++;
} else {
queue2.push(pack);
}
}
std::swap(queue, queue2);
// nothing done but deferring
if (addedInIteration == 0 && resolveWeaks) {
throw on_circular_dependency(queue);
}
resolveWeaks = true;
}
return added;
}
std::vector<std::string> PacksManager::getNames(const std::vector<ContentPack>& packs) {
std::vector<std::string> result;
for (const auto& pack : packs) {
result.push_back(pack.id);
}
return result;
}
+47
View File
@@ -0,0 +1,47 @@
#ifndef CONTENT_PACKS_MANAGER_HPP_
#define CONTENT_PACKS_MANAGER_HPP_
#include "ContentPack.hpp"
#include <vector>
#include <filesystem>
#include <unordered_map>
namespace fs = std::filesystem;
class PacksManager {
std::unordered_map<std::string, ContentPack> packs;
std::vector<fs::path> sources;
public:
PacksManager();
/// @brief Set content packs sources (search folders)
void setSources(std::vector<fs::path> sources);
/// @brief Scan sources and collect all found packs excluding duplication.
/// Scanning order depends on sources order
void scan();
/// @brief Remove pack from manager to make it invisible for assembly(...)
void exclude(const std::string& id);
/// @brief Get all found packs
std::vector<std::string> getAllNames() const;
/// @brief Get packs by names (id)
/// @param names pack names
/// @throws contentpack_error if pack not found
std::vector<ContentPack> getAll(const std::vector<std::string>& names) const;
/// @brief Resolve all dependencies and fix packs order
/// @param names required packs (method can add extra packs)
/// @return resulting ordered vector of pack names
/// @throws contentpack_error if required dependency not found or
/// circular dependency detected
std::vector<std::string> assembly(const std::vector<std::string>& names) const;
/// @brief Collect all pack names (identifiers) into a new vector
static std::vector<std::string> getNames(const std::vector<ContentPack>& packs);
};
#endif // CONTENT_PACKS_MANAGER_HPP_
+44 -43
View File
@@ -1,43 +1,44 @@
#include "core_defs.h"
#include "items/ItemDef.h"
#include "content/Content.h"
#include "window/Window.h"
#include "window/Events.h"
#include "window/input.h"
#include "voxels/Block.h"
// All in-game definitions (blocks, items, etc..)
void corecontent::setup(ContentBuilder* builder) {
Block& block = builder->createBlock("core:air");
block.replaceable = true;
block.drawGroup = 1;
block.lightPassing = true;
block.skyLightPassing = true;
block.obstacle = false;
block.selectable = false;
block.model = BlockModel::none;
block.pickingItem = "core:empty";
ItemDef& item = builder->createItem("core:empty");
item.iconType = item_icon_type::none;
}
void corecontent::setup_bindings() {
Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W);
Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S);
Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D);
Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A);
Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE);
Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL);
Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT);
Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R);
Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C);
Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4);
Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N);
Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F);
Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1);
Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2);
Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3);
Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB);
}
#include "core_defs.hpp"
#include "items/ItemDef.hpp"
#include "content/Content.hpp"
#include "window/Window.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "voxels/Block.hpp"
// All in-game definitions (blocks, items, etc..)
void corecontent::setup(ContentBuilder* builder) {
Block& block = builder->createBlock("core:air");
block.replaceable = true;
block.drawGroup = 1;
block.lightPassing = true;
block.skyLightPassing = true;
block.obstacle = false;
block.selectable = false;
block.model = BlockModel::none;
block.pickingItem = "core:empty";
ItemDef& item = builder->createItem("core:empty");
item.iconType = item_icon_type::none;
}
void corecontent::setup_bindings() {
Events::bind(BIND_DEVTOOLS_CONSOLE, inputtype::keyboard, keycode::GRAVE_ACCENT);
Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W);
Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S);
Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D);
Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A);
Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE);
Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL);
Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT);
Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R);
Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C);
Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4);
Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N);
Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F);
Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1);
Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2);
Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3);
Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB);
}
+7 -6
View File
@@ -1,5 +1,5 @@
#ifndef CORE_DEFS_H_
#define CORE_DEFS_H_
#ifndef CORE_DEFS_HPP_
#define CORE_DEFS_HPP_
#include <string>
@@ -8,7 +8,8 @@ inline const std::string CORE_AIR = "core:air";
inline const std::string TEXTURE_NOTFOUND = "notfound";
/* bindings used in engine code */
// built-in bindings
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console";
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
inline const std::string BIND_MOVE_BACK = "movement.back";
inline const std::string BIND_MOVE_LEFT = "movement.left";
@@ -29,8 +30,8 @@ inline const std::string BIND_HUD_INVENTORY = "hud.inventory";
class ContentBuilder;
namespace corecontent {
extern void setup_bindings();
extern void setup(ContentBuilder* builder);
void setup_bindings();
void setup(ContentBuilder* builder);
}
#endif // CORE_DEFS_H_
#endif // CORE_DEFS_HPP_
+152 -250
View File
@@ -1,145 +1,103 @@
#include "dynamic.h"
#include "dynamic.hpp"
#include <stdexcept>
#include "../coders/json.hpp"
using namespace dynamic;
List::~List() {
std::ostream& operator<<(std::ostream& stream, const dynamic::Value& value) {
stream << json::stringify(value, false, " ");
return stream;
}
std::ostream& operator<<(std::ostream& stream, const dynamic::Map_sptr& value) {
stream << json::stringify(value, false, " ");
return stream;
}
std::ostream& operator<<(std::ostream& stream, const dynamic::List_sptr& value) {
stream << json::stringify(value, false, " ");
return stream;
}
std::string List::str(size_t index) const {
const auto& val = values[index];
switch (val->type) {
case valtype::string: return *val->value.str;
case valtype::boolean: return val->value.boolean ? "true" : "false";
case valtype::number: return std::to_string(val->value.decimal);
case valtype::integer: return std::to_string(val->value.integer);
const auto& value = values[index];
switch (static_cast<Type>(value.index())) {
case Type::string: return std::get<std::string>(value);
case Type::boolean: return std::get<bool>(value) ? "true" : "false";
case Type::number: return std::to_string(std::get<double>(value));
case Type::integer: return std::to_string(std::get<int64_t>(value));
default:
throw std::runtime_error("type error");
}
}
double List::num(size_t index) const {
const auto& val = values[index];
switch (val->type) {
case valtype::number: return val->value.decimal;
case valtype::integer: return val->value.integer;
case valtype::string: return std::stoll(*val->value.str);
case valtype::boolean: return val->value.boolean;
number_t List::num(size_t index) const {
const auto& value = values[index];
switch (static_cast<Type>(value.index())) {
case Type::number: return std::get<number_t>(value);
case Type::integer: return std::get<integer_t>(value);
case Type::string: return std::stoll(std::get<std::string>(value));
case Type::boolean: return std::get<bool>(value);
default:
throw std::runtime_error("type error");
}
}
int64_t List::integer(size_t index) const {
const auto& val = values[index];
switch (val->type) {
case valtype::number: return val->value.decimal;
case valtype::integer: return val->value.integer;
case valtype::string: return std::stoll(*val->value.str);
case valtype::boolean: return val->value.boolean;
integer_t List::integer(size_t index) const {
const auto& value = values[index];
switch (static_cast<Type>(value.index())) {
case Type::number: return std::get<number_t>(value);
case Type::integer: return std::get<integer_t>(value);
case Type::string: return std::stoll(std::get<std::string>(value));
case Type::boolean: return std::get<bool>(value);
default:
throw std::runtime_error("type error");
}
}
Map* List::map(size_t index) const {
if (values[index]->type != valtype::map) {
if (auto* val = std::get_if<Map_sptr>(&values[index])) {
return val->get();
} else {
throw std::runtime_error("type error");
}
return values[index]->value.map;
}
List* List::list(size_t index) const {
if (values[index]->type != valtype::list) {
if (auto* val = std::get_if<List_sptr>(&values[index])) {
return val->get();
} else {
throw std::runtime_error("type error");
}
return values[index]->value.list;
}
bool List::flag(size_t index) const {
const auto& val = values[index];
switch (val->type) {
case valtype::integer: return val->value.integer;
case valtype::boolean: return val->value.boolean;
const auto& value = values[index];
switch (static_cast<Type>(value.index())) {
case Type::integer: return std::get<integer_t>(value);
case Type::boolean: return std::get<bool>(value);
default:
throw std::runtime_error("type error");
}
}
Value* List::getValueWriteable(size_t index) const {
if (index > values.size()) {
throw std::runtime_error("index error");
}
return values.at(index).get();
Value* List::getValueWriteable(size_t index) {
return &values.at(index);
}
List& List::put(std::string value) {
valvalue val;
val.str = new std::string(value);
values.push_back(std::make_unique<Value>(valtype::string, val));
return *this;
}
List& List::put(uint value) {
return put((int64_t)value);
}
List& List::put(int value) {
return put((int64_t)value);
}
List& List::put(int64_t value) {
valvalue val;
val.integer = value;
values.push_back(std::make_unique<Value>(valtype::integer, val));
return *this;
}
List& List::put(uint64_t value) {
return put((int64_t)value);
}
List& List::put(double value) {
valvalue val;
val.decimal = value;
values.push_back(std::make_unique<Value>(valtype::number, val));
return *this;
}
List& List::put(float value) {
return put((double)value);
}
List& List::put(bool value) {
valvalue val;
val.boolean = value;
values.push_back(std::make_unique<Value>(valtype::boolean, val));
return *this;
}
List& List::put(Map* value) {
valvalue val;
val.map = value;
values.push_back(std::make_unique<Value>(valtype::map, val));
return *this;
}
List& List::put(List* value) {
valvalue val;
val.list = value;
values.push_back(std::make_unique<Value>(valtype::list, val));
List& List::put(const Value& value) {
values.emplace_back(value);
return *this;
}
List& List::putList() {
List* arr = new List();
auto arr = create_list();
put(arr);
return *arr;
}
Map& List::putMap() {
Map* map = new Map();
auto map = create_map();
put(map);
return *map;
}
@@ -148,232 +106,176 @@ void List::remove(size_t index) {
values.erase(values.begin() + index);
}
Map::~Map() {
void Map::str(const std::string& key, std::string& dst) const {
dst = get(key, dst);
}
void Map::str(std::string key, std::string& dst) const {
dst = getStr(key, dst);
}
std::string Map::getStr(std::string key) const {
if (values.find(key) == values.end()) {
throw std::runtime_error("missing key '"+key+"'");
}
return getStr(key, "");
}
double Map::getNum(std::string key) const {
if (values.find(key) == values.end()) {
throw std::runtime_error("missing key '"+key+"'");
}
return getNum(key, 0);
}
int64_t Map::getInt(std::string key) const {
if (values.find(key) == values.end()) {
throw std::runtime_error("missing key '"+key+"'");
}
return getInt(key, 0);
}
bool Map::getBool(std::string key) const {
if (values.find(key) == values.end()) {
throw std::runtime_error("missing key '"+key+"'");
}
return getBool(key, false);
}
std::string Map::getStr(std::string key, const std::string& def) const {
std::string Map::get(const std::string& key, const std::string def) const {
auto found = values.find(key);
if (found == values.end())
return def;
auto& val = found->second;
switch (val->type) {
case valtype::string: return *val->value.str;
case valtype::boolean: return val->value.boolean ? "true" : "false";
case valtype::number: return std::to_string(val->value.decimal);
case valtype::integer: return std::to_string(val->value.integer);
default: throw std::runtime_error("type error");
}
}
double Map::getNum(std::string key, double def) const {
auto found = values.find(key);
if (found == values.end())
return def;
auto& val = found->second;
switch (val->type) {
case valtype::number: return val->value.decimal;
case valtype::integer: return val->value.integer;
case valtype::string: return std::stoull(*val->value.str);
case valtype::boolean: return val->value.boolean;
auto& value = found->second;
switch (static_cast<Type>(value.index())) {
case Type::string: return std::get<std::string>(value);
case Type::boolean: return std::get<bool>(value) ? "true" : "false";
case Type::number: return std::to_string(std::get<number_t>(value));
case Type::integer: return std::to_string(std::get<integer_t>(value));
default: throw std::runtime_error("type error");
}
}
int64_t Map::getInt(std::string key, int64_t def) const {
number_t Map::get(const std::string& key, double def) const {
auto found = values.find(key);
if (found == values.end())
return def;
auto& val = found->second;
switch (val->type) {
case valtype::number: return val->value.decimal;
case valtype::integer: return val->value.integer;
case valtype::string: return std::stoull(*val->value.str);
case valtype::boolean: return val->value.boolean;
auto& value = found->second;
switch (static_cast<Type>(value.index())) {
case Type::number: return std::get<number_t>(value);
case Type::integer: return std::get<integer_t>(value);
case Type::string: return std::stoull(std::get<std::string>(value));
case Type::boolean: return std::get<bool>(value);
default: throw std::runtime_error("type error");
}
}
bool Map::getBool(std::string key, bool def) const {
integer_t Map::get(const std::string& key, integer_t def) const {
auto found = values.find(key);
if (found == values.end())
return def;
auto& val = found->second;
switch (val->type) {
case valtype::integer: return val->value.integer;
case valtype::boolean: return val->value.boolean;
auto& value = found->second;
switch (static_cast<Type>(value.index())) {
case Type::number: return std::get<number_t>(value);
case Type::integer: return std::get<integer_t>(value);
case Type::string: return std::stoull(std::get<std::string>(value));
case Type::boolean: return std::get<bool>(value);
default: throw std::runtime_error("type error");
}
}
void Map::num(std::string key, double& dst) const {
dst = getNum(key, dst);
bool Map::get(const std::string& key, bool def) const {
auto found = values.find(key);
if (found == values.end())
return def;
auto& value = found->second;
switch (static_cast<Type>(value.index())) {
case Type::integer: return std::get<integer_t>(value);
case Type::boolean: return std::get<bool>(value);
default: throw std::runtime_error("type error");
}
}
void Map::num(std::string key, float& dst) const {
dst = getNum(key, dst);
void Map::num(const std::string& key, double& dst) const {
dst = get(key, dst);
}
void Map::num(std::string key, ubyte& dst) const {
dst = getInt(key, dst);
void Map::num(const std::string& key, float& dst) const {
dst = get(key, static_cast<number_t>(dst));
}
void Map::num(std::string key, int& dst) const {
dst = getInt(key, dst);
void Map::num(const std::string& key, ubyte& dst) const {
dst = get(key, static_cast<integer_t>(dst));
}
void Map::num(std::string key, int64_t& dst) const {
dst = getInt(key, dst);
void Map::num(const std::string& key, int& dst) const {
dst = get(key, static_cast<integer_t>(dst));
}
void Map::num(std::string key, uint64_t& dst) const {
dst = getInt(key, dst);
void Map::num(const std::string& key, int64_t& dst) const {
dst = get(key, dst);
}
void Map::num(std::string key, uint& dst) const {
dst = getInt(key, dst);
void Map::num(const std::string& key, uint64_t& dst) const {
dst = get(key, static_cast<integer_t>(dst));
}
Map* Map::map(std::string key) const {
void Map::num(const std::string& key, uint& dst) const {
dst = get(key, static_cast<integer_t>(dst));
}
Map* Map::map(const std::string& key) const {
auto found = values.find(key);
if (found != values.end()) {
auto& val = found->second;
if (val->type != valtype::map)
return nullptr;
return val->value.map;
if (auto* val = std::get_if<Map_sptr>(&found->second)) {
return val->get();
}
}
return nullptr;
}
List* Map::list(std::string key) const {
List* Map::list(const std::string& key) const {
auto found = values.find(key);
if (found != values.end())
return found->second->value.list;
return std::get<List_sptr>(found->second).get();
return nullptr;
}
void Map::flag(std::string key, bool& dst) const {
dst = getBool(key, dst);
void Map::flag(const std::string& key, bool& dst) const {
dst = get(key, dst);
}
Map& Map::put(std::string key, uint value) {
return put(key, (int64_t)value);
}
Map& Map::put(std::string key, int value) {
return put(key, (int64_t)value);
}
Map& Map::put(std::string key, int64_t value) {
valvalue val;
val.integer = value;
values[key] = std::make_unique<Value>(valtype::integer, val);
Map& Map::put(std::string key, const Value& value) {
values.emplace(key, value);
return *this;
}
Map& Map::put(std::string key, uint64_t value) {
return put(key, (int64_t)value);
}
Map& Map::put(std::string key, float value) {
return put(key, (double)value);
}
Map& Map::put(std::string key, double value) {
valvalue val;
val.decimal = value;
values[key] = std::make_unique<Value>(valtype::number, val);
return *this;
}
Map& Map::put(std::string key, std::string value){
valvalue val;
val.str = new std::string(value);
values[key] = std::make_unique<Value>(valtype::string, val);
return *this;
}
Map& Map::put(std::string key, const char* value) {
return put(key, std::string(value));
}
Map& Map::put(std::string key, Map* value){
valvalue val;
val.map = value;
values[key] = std::make_unique<Value>(valtype::map, val);
return *this;
}
Map& Map::put(std::string key, List* value){
valvalue val;
val.list = value;
values[key] = std::make_unique<Value>(valtype::list, val);
return *this;
}
Map& Map::put(std::string key, bool value){
valvalue val;
val.boolean = value;
values[key] = std::make_unique<Value>(valtype::boolean, val);
return *this;
void Map::remove(const std::string& key) {
values.erase(key);
}
List& Map::putList(std::string key) {
List* arr = new List();
auto arr = create_list();
put(key, arr);
return *arr;
}
Map& Map::putMap(std::string key) {
Map* obj = new Map();
auto obj = create_map();
put(key, obj);
return *obj;
}
bool Map::has(std::string key) {
bool Map::has(const std::string& key) const {
return values.find(key) != values.end();
}
Value::Value(valtype type, valvalue value) : type(type), value(value) {
size_t Map::size() const {
return values.size();
}
Value::~Value() {
switch (type) {
case valtype::map: delete value.map; break;
case valtype::list: delete value.list; break;
case valtype::string: delete value.str; break;
default:
break;
static const std::string TYPE_NAMES[] {
"none",
"map",
"list",
"string",
"number",
"bool",
"integer",
};
const std::string& dynamic::type_name(const Value& value) {
return TYPE_NAMES[value.index()];
}
List_sptr dynamic::create_list(std::initializer_list<Value> values) {
return std::make_shared<List>(values);
}
Map_sptr dynamic::create_map(std::initializer_list<std::pair<const std::string, Value>> entries) {
return std::make_shared<Map>(entries);
}
number_t dynamic::get_number(const Value& value) {
if (auto num = std::get_if<number_t>(&value)) {
return *num;
} else if (auto num = std::get_if<integer_t>(&value)) {
return *num;
}
throw std::runtime_error("cannot cast "+type_name(value)+" to number");
}
integer_t dynamic::get_integer(const Value& value) {
if (auto num = std::get_if<integer_t>(&value)) {
return *num;
}
throw std::runtime_error("cannot cast "+type_name(value)+" to integer");
}
-121
View File
@@ -1,121 +0,0 @@
#ifndef DATA_DYNAMIC_H_
#define DATA_DYNAMIC_H_
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include "../typedefs.h"
namespace dynamic {
class Map;
class List;
class Value;
enum class valtype {
map, list, number, integer, string, boolean
};
union valvalue {
Map* map;
List* list;
std::string* str;
double decimal;
int64_t integer;
uint64_t boolean;
};
class Value {
public:
valtype type;
valvalue value;
Value(valtype type, valvalue value);
~Value();
};
class List {
public:
std::vector<std::unique_ptr<Value>> values;
~List();
std::string str(size_t index) const;
double num(size_t index) const;
int64_t integer(size_t index) const;
Map* map(size_t index) const;
List* list(size_t index) const;
bool flag(size_t index) const;
inline size_t size() const {
return values.size();
}
inline Value* get(size_t i) const {
return values.at(i).get();
}
List& put(uint value);
List& put(int value);
List& put(uint64_t value);
List& put(int64_t value);
List& put(float value);
List& put(double value);
List& put(std::string value);
List& put(Map* value);
List& put(List* value);
List& put(bool value);
Value* getValueWriteable(size_t index) const;
List& putList();
Map& putMap();
void remove(size_t index);
};
class Map {
public:
std::unordered_map<std::string, std::unique_ptr<Value>> values;
~Map();
std::string getStr(std::string key) const;
double getNum(std::string key) const;
int64_t getInt(std::string key) const;
bool getBool(std::string key) const;
std::string getStr(std::string key, const std::string& def) const;
double getNum(std::string key, double def) const;
int64_t getInt(std::string key, int64_t def) const;
bool getBool(std::string key, bool def) const;
void str(std::string key, std::string& dst) const;
void num(std::string key, int& dst) const;
void num(std::string key, float& dst) const;
void num(std::string key, uint& dst) const;
void num(std::string key, int64_t& dst) const;
void num(std::string key, uint64_t& dst) const;
void num(std::string key, ubyte& dst) const;
void num(std::string key, double& dst) const;
Map* map(std::string key) const;
List* list(std::string key) const;
void flag(std::string key, bool& dst) const;
Map& put(std::string key, uint value);
Map& put(std::string key, int value);
Map& put(std::string key, int64_t value);
Map& put(std::string key, uint64_t value);
Map& put(std::string key, float value);
Map& put(std::string key, double value);
Map& put(std::string key, const char* value);
Map& put(std::string key, std::string value);
Map& put(std::string key, Map* value);
Map& put(std::string key, List* value);
Map& put(std::string key, bool value);
List& putList(std::string key);
Map& putMap(std::string key);
bool has(std::string key);
};
}
#endif // DATA_DYNAMIC_H_
+170
View File
@@ -0,0 +1,170 @@
#ifndef DATA_DYNAMIC_HPP_
#define DATA_DYNAMIC_HPP_
#include "../typedefs.hpp"
#include <string>
#include <vector>
#include <memory>
#include <variant>
#include <stdexcept>
#include <unordered_map>
namespace dynamic {
class Map;
class List;
enum class Type {
none=0, map, list, string, number, boolean, integer
};
using Map_sptr = std::shared_ptr<Map>;
using List_sptr = std::shared_ptr<List>;
struct none {};
inline constexpr none NONE = {};
using Value = std::variant<
none,
Map_sptr,
List_sptr,
std::string,
number_t,
bool,
integer_t
>;
const std::string& type_name(const Value& value);
List_sptr create_list(std::initializer_list<Value> values={});
Map_sptr create_map(std::initializer_list<std::pair<const std::string, Value>> entries={});
number_t get_number(const Value& value);
integer_t get_integer(const Value& value);
inline bool is_numeric(const Value& value) {
return std::holds_alternative<number_t>(value) ||
std::holds_alternative<integer_t>(value);
}
class List {
public:
std::vector<Value> values;
List() {}
List(std::vector<Value> values) : values(std::move(values)) {}
std::string str(size_t index) const;
number_t num(size_t index) const;
integer_t integer(size_t index) const;
Map* map(size_t index) const;
List* list(size_t index) const;
bool flag(size_t index) const;
inline size_t size() const {
return values.size();
}
inline Value& get(size_t i) {
return values.at(i);
}
List& put(std::unique_ptr<Map> value) {
return put(Map_sptr(value.release()));
}
List& put(std::unique_ptr<List> value) {
return put(List_sptr(value.release()));
}
List& put(const Value& value);
Value* getValueWriteable(size_t index);
List& putList();
Map& putMap();
void remove(size_t index);
};
class Map {
public:
std::unordered_map<std::string, Value> values;
Map() {}
Map(std::unordered_map<std::string, Value> values)
: values(std::move(values)) {};
template<typename T>
T get(const std::string& key) const {
if (!has(key)) {
throw std::runtime_error("missing key '"+key+"'");
}
return get(key, T());
}
std::string get(const std::string& key, const std::string def) const;
number_t get(const std::string& key, double def) const;
integer_t get(const std::string& key, integer_t def) const;
bool get(const std::string& key, bool def) const;
int get(const std::string& key, int def) const {
return get(key, static_cast<integer_t>(def));
}
uint get(const std::string& key, uint def) const {
return get(key, static_cast<integer_t>(def));
}
uint64_t get(const std::string& key, uint64_t def) const {
return get(key, static_cast<integer_t>(def));
}
void str(const std::string& key, std::string& dst) const;
void num(const std::string& key, int& dst) const;
void num(const std::string& key, float& dst) const;
void num(const std::string& key, uint& dst) const;
void num(const std::string& key, int64_t& dst) const;
void num(const std::string& key, uint64_t& dst) const;
void num(const std::string& key, ubyte& dst) const;
void num(const std::string& key, double& dst) const;
Map* map(const std::string& key) const;
List* list(const std::string& key) const;
void flag(const std::string& key, bool& dst) const;
Map& put(std::string key, std::unique_ptr<Map> value) {
return put(key, Map_sptr(value.release()));
}
Map& put(std::string key, std::unique_ptr<List> value) {
return put(key, List_sptr(value.release()));
}
Map& put(std::string key, int value) {
return put(key, Value(static_cast<integer_t>(value)));
}
Map& put(std::string key, unsigned int value) {
return put(key, Value(static_cast<integer_t>(value)));
}
Map& put(std::string key, int64_t value) {
return put(key, Value(static_cast<integer_t>(value)));
}
Map& put(std::string key, float value) {
return put(key, Value(static_cast<number_t>(value)));
}
Map& put(std::string key, double value) {
return put(key, Value(static_cast<number_t>(value)));
}
Map& put(std::string key, bool value) {
return put(key, Value(static_cast<bool>(value)));
}
Map& put(std::string key, const Value& value);
void remove(const std::string& key);
List& putList(std::string key);
Map& putMap(std::string key);
bool has(const std::string& key) const;
size_t size() const;
};
}
std::ostream& operator<<(std::ostream& stream, const dynamic::Value& value);
std::ostream& operator<<(std::ostream& stream, const dynamic::Map_sptr& value);
std::ostream& operator<<(std::ostream& stream, const dynamic::List_sptr& value);
#endif // DATA_DYNAMIC_HPP_
+38
View File
@@ -0,0 +1,38 @@
#include "setting.hpp"
#include "../util/stringutil.hpp"
std::string NumberSetting::toString() const {
switch (getFormat()) {
case setting_format::simple:
return util::to_string(value);
case setting_format::percent:
return std::to_string(static_cast<integer_t>(round(value * 100))) + "%";
default:
return "invalid format";
}
}
std::string IntegerSetting::toString() const {
switch (getFormat()) {
case setting_format::simple:
return util::to_string(value);
case setting_format::percent:
return std::to_string(value) + "%";
default:
return "invalid format";
}
}
std::string FlagSetting::toString() const {
switch (getFormat()) {
case setting_format::simple:
return value ? "true" : "false";
default:
return "invalid format";
}
}
std::string StringSetting::toString() const {
return value;
}
+180
View File
@@ -0,0 +1,180 @@
#ifndef DATA_SETTING_HPP_
#define DATA_SETTING_HPP_
#include <limits>
#include <string>
#include <vector>
#include <unordered_map>
#include "../typedefs.hpp"
#include "../delegates.hpp"
enum class setting_format {
simple, percent
};
class Setting {
protected:
setting_format format;
public:
Setting(setting_format format) : format(format) {
}
virtual ~Setting() {}
virtual void resetToDefault() = 0;
virtual setting_format getFormat() const {
return format;
}
virtual std::string toString() const = 0;
};
template<class T>
class ObservableSetting : public Setting {
int nextid = 1;
std::unordered_map<int, consumer<T>> observers;
protected:
T initial;
T value;
public:
ObservableSetting(T value, setting_format format)
: Setting(format), initial(value), value(value) {}
observer_handler observe(consumer<T> callback, bool callOnStart=false) {
const int id = nextid++;
observers.emplace(id, callback);
if (callOnStart) {
callback(value);
}
return std::shared_ptr<int>(new int(id), [this](int* id) {
observers.erase(*id);
delete id;
});
}
const T& get() const {
return value;
}
T& operator*() {
return value;
}
void notify(T value) {
for (auto& entry : observers) {
entry.second(value);
}
}
void set(T value) {
if (value == this->value) {
return;
}
this->value = value;
notify(value);
}
virtual void resetToDefault() override {
set(initial);
}
};
class NumberSetting : public ObservableSetting<number_t> {
protected:
number_t min;
number_t max;
public:
NumberSetting(
number_t value,
number_t min=std::numeric_limits<number_t>::min(),
number_t max=std::numeric_limits<number_t>::max(),
setting_format format=setting_format::simple
) : ObservableSetting(value, format),
min(min),
max(max)
{}
number_t& operator*() {
return value;
}
number_t get() const {
return value;
}
number_t getMin() const {
return min;
}
number_t getMax() const {
return max;
}
number_t getT() const {
return (value - min) / (max - min);
}
virtual std::string toString() const override;
static inline NumberSetting createPercent(number_t def) {
return NumberSetting(def, 0.0, 1.0, setting_format::percent);
}
};
class IntegerSetting : public ObservableSetting<integer_t> {
protected:
integer_t min;
integer_t max;
public:
IntegerSetting(
integer_t value,
integer_t min=std::numeric_limits<integer_t>::min(),
integer_t max=std::numeric_limits<integer_t>::max(),
setting_format format=setting_format::simple
) : ObservableSetting(value, format),
min(min),
max(max)
{}
integer_t getMin() const {
return min;
}
integer_t getMax() const {
return max;
}
integer_t getT() const {
return (value - min) / (max - min);
}
virtual std::string toString() const override;
};
class FlagSetting : public ObservableSetting<bool> {
public:
FlagSetting(
bool value,
setting_format format=setting_format::simple
) : ObservableSetting(value, format) {}
void toggle() {
set(!get());
}
virtual std::string toString() const override;
};
class StringSetting : public ObservableSetting<std::string> {
public:
StringSetting(
std::string value,
setting_format format=setting_format::simple
) : ObservableSetting(value, format) {}
virtual std::string toString() const override;
};
#endif // DATA_SETTING_HPP_
+75
View File
@@ -0,0 +1,75 @@
#include "Logger.hpp"
#include <ctime>
#include <iomanip>
#include <chrono>
#include <iostream>
using namespace debug;
std::ofstream Logger::file;
std::mutex Logger::mutex;
std::string Logger::utcOffset = "";
unsigned Logger::moduleLen = 20;
LogMessage::~LogMessage() {
logger->log(level, ss.str());
}
Logger::Logger(std::string name) : name(name) {
}
void Logger::log(LogLevel level, const std::string& name, std::string message) {
using namespace std::chrono;
std::stringstream ss;
switch (level) {
case LogLevel::debug:
# ifdef NDEBUG
return;
# endif
ss << "[D]";
break;
case LogLevel::info:
ss << "[I]";
break;
case LogLevel::warning:
ss << "[W]";
break;
case LogLevel::error:
ss << "[E]";
break;
}
time_t tm = std::time(nullptr);
auto ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000;
ss << " " << std::put_time(std::localtime(&tm), "%Y/%m/%d %T");
ss << '.' << std::setfill('0') << std::setw(3) << ms.count();
ss << utcOffset << " (" << std::setfill(' ') << std::setw(moduleLen) << name << ") ";
ss << message;
{
std::lock_guard<std::mutex> lock(mutex);
auto string = ss.str();
if (file.good()) {
file << string << '\n';
}
std::cout << string << std::endl;
}
}
void Logger::init(const std::string& filename) {
file.open(filename);
time_t tm = std::time(nullptr);
std::stringstream ss;
ss << std::put_time(std::localtime(&tm), "%z");
utcOffset = ss.str();
}
void Logger::flush() {
std::lock_guard<std::mutex> lock(mutex);
file.flush();
}
void Logger::log(LogLevel level, std::string message) {
log(level, name, message);
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef DEBUG_LOGGER_HPP_
#define DEBUG_LOGGER_HPP_
#include <sstream>
#include <fstream>
#include <mutex>
namespace debug {
enum class LogLevel {
debug, info, warning, error
};
class Logger;
class LogMessage {
Logger* logger;
LogLevel level;
std::stringstream ss;
public:
LogMessage(Logger* logger, LogLevel level) : logger(logger), level(level) {}
~LogMessage();
template<class T>
LogMessage& operator<<(const T& x) {
ss << x;
return *this;
}
};
class Logger {
static std::mutex mutex;
static std::string utcOffset;
static std::ofstream file;
static unsigned moduleLen;
std::string name;
static void log(LogLevel level, const std::string& name, std::string message);
public:
static void init(const std::string& filename);
static void flush();
Logger(std::string name);
void log(LogLevel level, std::string message);
LogMessage debug() {
return LogMessage(this, LogLevel::debug);
}
LogMessage info() {
return LogMessage(this, LogLevel::info);
}
LogMessage error() {
return LogMessage(this, LogLevel::error);
}
LogMessage warning() {
return LogMessage(this, LogLevel::warning);
}
};
}
#endif // DEBUG_LOGGER_HPP_
+5 -3
View File
@@ -1,11 +1,13 @@
#ifndef DELEGATES_H_
#define DELEGATES_H_
#ifndef DELEGATES_HPP_
#define DELEGATES_HPP_
#include <glm/glm.hpp>
#include <functional>
#include <string>
using runnable = std::function<void()>;
template<class T> using supplier = std::function<T()>;
template<class T> using consumer = std::function<void(T)>;
// data sources
using wstringsupplier = std::function<std::wstring()>;
@@ -20,4 +22,4 @@ using boolconsumer = std::function<void(bool)>;
using int_array_consumer = std::function<void(const int[], size_t)>;
using wstringchecker = std::function<bool(const std::wstring&)>;
#endif // DELEGATES_H_
#endif // DELEGATES_HPP_
+238 -168
View File
@@ -1,47 +1,47 @@
#include "engine.h"
#include "engine.hpp"
#include <memory>
#include <iostream>
#include <assert.h>
#include <vector>
#include <glm/glm.hpp>
#include <filesystem>
#include <unordered_set>
#include <functional>
#define GLEW_STATIC
#include "audio/audio.h"
#include "assets/Assets.h"
#include "assets/AssetsLoader.h"
#include "world/WorldGenerators.h"
#include "voxels/DefaultWorldGenerator.h"
#include "voxels/FlatWorldGenerator.h"
#include "window/Window.h"
#include "window/Events.h"
#include "window/Camera.h"
#include "window/input.h"
#include "graphics/Batch2D.h"
#include "graphics/GfxContext.h"
#include "graphics/Shader.h"
#include "graphics/ImageData.h"
#include "frontend/gui/GUI.h"
#include "frontend/screens.h"
#include "frontend/menu/menu.h"
#include "util/platform.h"
#include "debug/Logger.hpp"
#include "assets/AssetsLoader.hpp"
#include "audio/audio.hpp"
#include "coders/GLSLExtension.hpp"
#include "coders/imageio.hpp"
#include "coders/json.hpp"
#include "coders/toml.hpp"
#include "content/ContentLoader.hpp"
#include "core_defs.hpp"
#include "files/files.hpp"
#include "files/settings_io.hpp"
#include "frontend/locale.hpp"
#include "frontend/menu.hpp"
#include "frontend/screens/Screen.hpp"
#include "frontend/screens/MenuScreen.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/ImageData.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/ui/GUI.hpp"
#include "logic/EngineController.hpp"
#include "logic/CommandsInterpreter.hpp"
#include "logic/scripting/scripting.hpp"
#include "util/listutil.hpp"
#include "util/platform.hpp"
#include "voxels/DefaultWorldGenerator.hpp"
#include "voxels/FlatWorldGenerator.hpp"
#include "window/Camera.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "window/Window.hpp"
#include "world/WorldGenerators.hpp"
#include "coders/json.h"
#include "coders/png.h"
#include "coders/GLSLExtension.h"
#include "files/files.h"
#include "files/engine_paths.h"
#include <iostream>
#include <assert.h>
#include <glm/glm.hpp>
#include <unordered_set>
#include <functional>
#include "content/Content.h"
#include "content/ContentPack.h"
#include "content/ContentLoader.h"
#include "frontend/locale/langs.h"
#include "logic/scripting/scripting.h"
#include "core_defs.h"
static debug::Logger logger("engine");
namespace fs = std::filesystem;
@@ -50,48 +50,68 @@ void addWorldGenerators() {
WorldGenerators::addGenerator<FlatWorldGenerator>("core:flat");
}
Engine::Engine(EngineSettings& settings, EnginePaths* paths)
: settings(settings), paths(paths)
{
if (Window::initialize(settings.display)){
inline void create_channel(Engine* engine, std::string name, NumberSetting& setting) {
if (name != "master") {
audio::create_channel(name);
}
engine->keepAlive(setting.observe([=](auto value) {
audio::get_channel(name)->setVolume(value*value);
}));
}
Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths)
: settings(settings), settingsHandler(settingsHandler), paths(paths),
interpreter(std::make_unique<cmd::CommandsInterpreter>())
{
corecontent::setup_bindings();
loadSettings();
controller = std::make_unique<EngineController>(this);
if (Window::initialize(&this->settings.display)){
throw initialize_error("could not initialize window");
}
audio::initialize(settings.audio.enabled);
audio::create_channel("regular");
audio::create_channel("music");
audio::create_channel("ambient");
audio::create_channel("ui");
audio::initialize(settings.audio.enabled.get());
create_channel(this, "master", settings.audio.volumeMaster);
create_channel(this, "regular", settings.audio.volumeRegular);
create_channel(this, "music", settings.audio.volumeMusic);
create_channel(this, "ambient", settings.audio.volumeAmbient);
create_channel(this, "ui", settings.audio.volumeUI);
auto resdir = paths->getResources();
scripting::initialize(this);
std::cout << "-- loading assets" << std::endl;
std::vector<fs::path> roots {resdir};
resPaths = std::make_unique<ResPaths>(resdir, roots);
assets = std::make_unique<Assets>();
AssetsLoader loader(assets.get(), resPaths.get());
AssetsLoader::addDefaults(loader, nullptr);
Shader::preprocessor->setPaths(resPaths.get());
while (loader.hasNext()) {
if (!loader.loadNext()) {
assets.reset();
scripting::close();
Window::terminate();
throw initialize_error("could not to load assets");
}
}
gui = std::make_unique<gui::GUI>();
if (settings.ui.language == "auto") {
settings.ui.language = langs::locale_by_envlocale(platform::detect_locale(), paths->getResources());
if (settings.ui.language.get() == "auto") {
settings.ui.language.set(langs::locale_by_envlocale(
platform::detect_locale(),
paths->getResources()
));
}
if (ENGINE_VERSION_INDEV) {
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(this);
}
setLanguage(settings.ui.language);
keepAlive(settings.ui.language.observe([=](auto lang) {
setLanguage(lang);
}, true));
addWorldGenerators();
scripting::initialize(this);
}
void Engine::loadSettings() {
fs::path settings_file = paths->getSettingsFile();
if (fs::is_regular_file(settings_file)) {
logger.info() << "loading settings";
std::string text = files::read_string(settings_file);
toml::parse(settingsHandler, settings_file.string(), text);
}
fs::path controls_file = paths->getControlsFile();
if (fs::is_regular_file(controls_file)) {
logger.info() << "loading controls";
std::string text = files::read_string(controls_file);
Events::loadBindings(controls_file.u8string(), text);
}
}
void Engine::onAssetsLoaded() {
gui->onAssetsLoad(assets.get());
}
void Engine::updateTimers() {
@@ -103,86 +123,132 @@ void Engine::updateTimers() {
void Engine::updateHotkeys() {
if (Events::jpressed(keycode::F2)) {
std::unique_ptr<ImageData> image(Window::takeScreenshot());
image->flipY();
fs::path filename = paths->getScreenshotFile("png");
png::write_image(filename.string(), image.get());
std::cout << "saved screenshot as " << filename << std::endl;
saveScreenshot();
}
if (Events::jpressed(keycode::F11)) {
Window::toggleFullscreen();
settings.display.fullscreen.toggle();
}
}
inline constexpr float sqr(float x) {
return x*x;
}
static void updateAudio(double delta, const AudioSettings& settings) {
audio::get_channel("master")->setVolume(sqr(settings.volumeMaster));
audio::get_channel("regular")->setVolume(sqr(settings.volumeRegular));
audio::get_channel("ui")->setVolume(sqr(settings.volumeUI));
audio::get_channel("ambient")->setVolume(sqr(settings.volumeAmbient));
audio::get_channel("music")->setVolume(sqr(settings.volumeMusic));
audio::update(delta);
void Engine::saveScreenshot() {
auto image = Window::takeScreenshot();
image->flipY();
fs::path filename = paths->getScreenshotFile("png");
imageio::write(filename.string(), image.get());
logger.info() << "saved screenshot as " << filename.u8string();
}
void Engine::mainloop() {
logger.info() << "starting menu screen";
setScreen(std::make_shared<MenuScreen>(this));
Batch2D batch(1024);
lastTime = Window::time();
std::cout << "-- initialized" << std::endl;
logger.info() << "engine started";
while (!Window::isShouldClose()){
assert(screen != nullptr);
updateTimers();
updateHotkeys();
updateAudio(delta, settings.audio);
audio::update(delta);
gui->act(delta);
gui->act(delta, Viewport(Window::width, Window::height));
screen->update(delta);
if (!Window::isIconified()) {
screen->draw(delta);
Viewport viewport(Window::width, Window::height);
GfxContext ctx(nullptr, viewport, &batch);
gui->draw(&ctx, assets.get());
Window::swapInterval(settings.display.swapInterval);
} else {
Window::swapInterval(1);
renderFrame(batch);
}
Window::swapInterval(Window::isIconified() ? 1 : settings.display.vsync.get());
processPostRunnables();
Window::swapBuffers();
Events::pollEvents();
}
}
Engine::~Engine() {
std::cout << "-- shutting down" << std::endl;
if (screen) {
screen->onEngineShutdown();
}
screen.reset();
content.reset();
assets.reset();
audio::close();
scripting::close();
Window::terminate();
std::cout << "-- engine finished" << std::endl;
void Engine::renderFrame(Batch2D& batch) {
screen->draw(delta);
Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, &batch);
gui->draw(&ctx, assets.get());
}
inline const std::string checkPacks(
const std::unordered_set<std::string>& packs,
const std::vector<std::string>& dependencies
) {
for (const std::string& str : dependencies) {
if (packs.find(str) == packs.end()) {
return str;
void Engine::processPostRunnables() {
std::lock_guard<std::recursive_mutex> lock(postRunnablesMutex);
while (!postRunnables.empty()) {
postRunnables.front()();
postRunnables.pop();
}
scripting::process_post_runnables();
}
void Engine::saveSettings() {
logger.info() << "saving settings";
files::write_string(paths->getSettingsFile(), toml::stringify(settingsHandler));
logger.info() << "saving bindings";
files::write_string(paths->getControlsFile(), Events::writeBindings());
}
Engine::~Engine() {
saveSettings();
logger.info() << "shutting down";
if (screen) {
screen->onEngineShutdown();
screen.reset();
}
content.reset();
assets.reset();
interpreter.reset();
gui.reset();
logger.info() << "gui finished";
audio::close();
scripting::close();
logger.info() << "scripting finished";
Window::terminate();
logger.info() << "engine finished";
}
EngineController* Engine::getController() {
return controller.get();
}
cmd::CommandsInterpreter* Engine::getCommandsInterpreter() {
return interpreter.get();
}
PacksManager Engine::createPacksManager(const fs::path& worldFolder) {
PacksManager manager;
manager.setSources({
worldFolder/fs::path("content"),
paths->getUserfiles()/fs::path("content"),
paths->getResources()/fs::path("content")
});
return manager;
}
void Engine::loadAssets() {
logger.info() << "loading assets";
Shader::preprocessor->setPaths(resPaths.get());
auto new_assets = std::make_unique<Assets>();
AssetsLoader loader(new_assets.get(), resPaths.get());
AssetsLoader::addDefaults(loader, content.get());
bool threading = false;
if (threading) {
auto task = loader.startTask([=](){});
task->waitForEnd();
} else {
while (loader.hasNext()) {
if (!loader.loadNext()) {
new_assets.reset();
throw std::runtime_error("could not to load assets");
}
}
}
return "";
assets.reset(new_assets.release());
}
void Engine::loadContent() {
@@ -191,65 +257,57 @@ void Engine::loadContent() {
corecontent::setup(&contentBuilder);
paths->setContentPacks(&contentPacks);
std::vector<fs::path> resRoots;
std::vector<ContentPack> srcPacks = contentPacks;
contentPacks.clear();
std::string missingDependency;
std::unordered_set<std::string> loadedPacks, existingPacks;
for (const auto& item : srcPacks) {
existingPacks.insert(item.id);
std::vector<std::string> names;
for (auto& pack : contentPacks) {
names.push_back(pack.id);
}
PacksManager manager = createPacksManager(paths->getWorldFolder());
manager.scan();
names = manager.assembly(names);
contentPacks = manager.getAll(names);
while (existingPacks.size() > loadedPacks.size()) {
for (auto& pack : srcPacks) {
if(loadedPacks.find(pack.id) != loadedPacks.end()) {
continue;
}
missingDependency = checkPacks(existingPacks, pack.dependencies);
if (!missingDependency.empty()) {
throw contentpack_error(pack.id, pack.folder, "missing dependency '"+missingDependency+"'");
}
if (pack.dependencies.empty() || checkPacks(loadedPacks, pack.dependencies).empty()) {
loadedPacks.insert(pack.id);
resRoots.push_back(pack.folder);
contentPacks.push_back(pack);
ContentLoader loader(&pack);
loader.load(contentBuilder);
}
}
std::vector<PathsRoot> resRoots;
for (auto& pack : contentPacks) {
resRoots.push_back({pack.id, pack.folder});
ContentLoader loader(&pack);
loader.load(contentBuilder);
}
content.reset(contentBuilder.build());
resPaths.reset(new ResPaths(resdir, resRoots));
resPaths = std::make_unique<ResPaths>(resdir, resRoots);
Shader::preprocessor->setPaths(resPaths.get());
langs::setup(resdir, langs::current->getId(), contentPacks);
loadAssets();
onAssetsLoaded();
}
std::unique_ptr<Assets> new_assets(new Assets());
std::cout << "-- loading assets" << std::endl;
AssetsLoader loader(new_assets.get(), resPaths.get());
AssetsLoader::addDefaults(loader, content.get());
while (loader.hasNext()) {
if (!loader.loadNext()) {
new_assets.reset();
throw std::runtime_error("could not to load assets");
}
}
assets->extend(*new_assets.get());
void Engine::resetContent() {
auto manager = createPacksManager(fs::path());
manager.scan();
contentPacks = manager.getAll(basePacks);
loadContent();
}
void Engine::loadWorldContent(const fs::path& folder) {
contentPacks.clear();
auto packNames = ContentPack::worldPacksList(folder);
ContentPack::readPacks(paths, contentPacks, packNames, folder);
PacksManager manager;
manager.setSources({
folder/fs::path("content"),
paths->getUserfiles()/fs::path("content"),
paths->getResources()/fs::path("content")
});
manager.scan();
contentPacks = manager.getAll(manager.assembly(packNames));
paths->setWorldFolder(folder);
loadContent();
}
void Engine::loadAllPacks() {
auto resdir = paths->getResources();
contentPacks.clear();
ContentPack::scan(paths, contentPacks);
PacksManager manager = createPacksManager(paths->getWorldFolder());
manager.scan();
auto allnames = manager.getAllNames();
contentPacks = manager.getAll(manager.assembly(allnames));
}
double Engine::getDelta() const {
@@ -263,9 +321,8 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
}
void Engine::setLanguage(std::string locale) {
settings.ui.language = locale;
langs::setup(paths->getResources(), locale, contentPacks);
menus::create_menus(this);
gui->getMenu()->setPageLoader(menus::create_page_loader(this));
}
gui::GUI* Engine::getGUI() {
@@ -288,6 +345,10 @@ std::vector<ContentPack>& Engine::getContentPacks() {
return contentPacks;
}
std::vector<std::string>& Engine::getBasePacks() {
return basePacks;
}
EnginePaths* Engine::getPaths() {
return paths;
}
@@ -299,3 +360,12 @@ ResPaths* Engine::getResPaths() {
std::shared_ptr<Screen> Engine::getScreen() {
return screen;
}
void Engine::postRunnable(runnable callback) {
std::lock_guard<std::recursive_mutex> lock(postRunnablesMutex);
postRunnables.push(callback);
}
SettingsHandler& Engine::getSettingsHandler() {
return settingsHandler;
}
+61 -18
View File
@@ -1,23 +1,32 @@
#ifndef SRC_ENGINE_H_
#define SRC_ENGINE_H_
#ifndef ENGINE_HPP_
#define ENGINE_HPP_
#include "delegates.hpp"
#include "settings.hpp"
#include "typedefs.hpp"
#include "assets/Assets.hpp"
#include "content/Content.hpp"
#include "content/ContentPack.hpp"
#include "content/PacksManager.hpp"
#include "files/engine_paths.hpp"
#include "util/ObjectsKeeper.hpp"
#include <string>
#include <memory>
#include <vector>
#include <stdexcept>
#include <filesystem>
#include "typedefs.h"
#include "settings.h"
#include "assets/Assets.h"
#include "content/Content.h"
#include "content/ContentPack.h"
#include "files/engine_paths.h"
#include <memory>
#include <queue>
#include <stdexcept>
#include <string>
#include <vector>
#include <mutex>
class Level;
class Screen;
class EnginePaths;
class ResPaths;
class Batch2D;
class EngineController;
class SettingsHandler;
namespace fs = std::filesystem;
@@ -25,19 +34,30 @@ namespace gui {
class GUI;
}
namespace cmd {
class CommandsInterpreter;
}
class initialize_error : public std::runtime_error {
public:
initialize_error(const std::string& message) : std::runtime_error(message) {}
};
class Engine {
class Engine : public util::ObjectsKeeper {
EngineSettings& settings;
SettingsHandler& settingsHandler;
EnginePaths* paths;
std::unique_ptr<Assets> assets = nullptr;
std::shared_ptr<Screen> screen = nullptr;
std::vector<ContentPack> contentPacks;
EngineSettings& settings;
std::unique_ptr<Content> content = nullptr;
EnginePaths* paths;
std::unique_ptr<ResPaths> resPaths = nullptr;
std::queue<runnable> postRunnables;
std::recursive_mutex postRunnablesMutex;
std::unique_ptr<EngineController> controller;
std::unique_ptr<cmd::CommandsInterpreter> interpreter;
std::vector<std::string> basePacks {"base"};
uint64_t frame = 0;
double lastTime = 0.0;
@@ -45,16 +65,23 @@ class Engine {
std::unique_ptr<gui::GUI> gui;
void loadSettings();
void saveSettings();
void updateTimers();
void updateHotkeys();
void renderFrame(Batch2D& batch);
void processPostRunnables();
void loadAssets();
public:
Engine(EngineSettings& settings, EnginePaths* paths);
Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths);
~Engine();
/// @brief Start main engine input/update/render loop.
/// Automatically sets MenuScreen
void mainloop();
/// @brief Called after assets loading when all engine systems are initialized
void onAssetsLoaded();
/// @brief Set screen (scene).
/// nullptr may be used to delete previous screen before creating new one,
@@ -68,6 +95,8 @@ public:
/// @brief Load all selected content-packs and reload assets
void loadContent();
void resetContent();
/// @brief Collect world content-packs and load content
/// @see loadContent
@@ -101,8 +130,22 @@ public:
/// @brief Get selected content packs
std::vector<ContentPack>& getContentPacks();
std::vector<std::string>& getBasePacks();
/// @brief Get current screen
std::shared_ptr<Screen> getScreen();
/// @brief Enqueue function call to the end of current frame in draw thread
void postRunnable(runnable callback);
void saveScreenshot();
EngineController* getController();
cmd::CommandsInterpreter* getCommandsInterpreter();
PacksManager createPacksManager(const fs::path& worldFolder);
SettingsHandler& getSettingsHandler();
};
#endif // SRC_ENGINE_H_
#endif // ENGINE_HPP_
+117 -45
View File
@@ -1,30 +1,46 @@
#include "WorldConverter.h"
#include "WorldConverter.hpp"
#include "WorldFiles.hpp"
#include "../content/ContentLUT.hpp"
#include "../data/dynamic.hpp"
#include "../debug/Logger.hpp"
#include "../files/files.hpp"
#include "../objects/Player.hpp"
#include "../util/ThreadPool.hpp"
#include "../voxels/Chunk.hpp"
#include <memory>
#include <iostream>
#include <stdexcept>
#include "WorldFiles.h"
#include "../data/dynamic.h"
#include "../files/files.h"
#include "../voxels/Chunk.h"
#include "../content/ContentLUT.h"
#include "../objects/Player.h"
namespace fs = std::filesystem;
static debug::Logger logger("world-converter");
class ConverterWorker : public util::Worker<convert_task, int> {
std::shared_ptr<WorldConverter> converter;
public:
ConverterWorker(std::shared_ptr<WorldConverter> converter)
: converter(converter) {}
int operator()(const std::shared_ptr<convert_task>& task) override {
converter->convert(*task);
return 0;
}
};
WorldConverter::WorldConverter(
fs::path folder,
const Content* content,
std::shared_ptr<ContentLUT> lut
) : lut(lut), content(content)
) : wfile(std::make_unique<WorldFiles>(folder)),
lut(lut),
content(content)
{
DebugSettings settings;
wfile = new WorldFiles(folder, settings);
fs::path regionsFolder = wfile->getRegionsFolder();
fs::path regionsFolder = wfile->getRegions().getRegionsFolder(REGION_LAYER_VOXELS);
if (!fs::is_directory(regionsFolder)) {
std::cerr << "nothing to convert" << std::endl;
logger.error() << "nothing to convert";
return;
}
tasks.push(convert_task {convert_task_type::player, wfile->getPlayerFile()});
@@ -34,52 +50,68 @@ WorldConverter::WorldConverter(
}
WorldConverter::~WorldConverter() {
delete wfile;
}
bool WorldConverter::hasNext() const {
return !tasks.empty();
std::shared_ptr<Task> WorldConverter::startTask(
fs::path folder,
const Content* content,
std::shared_ptr<ContentLUT> lut,
runnable onDone,
bool multithreading
) {
auto converter = std::make_shared<WorldConverter>(folder, content, lut);
if (!multithreading) {
converter->setOnComplete([=]() {
converter->write();
onDone();
});
return converter;
}
auto pool = std::make_shared<util::ThreadPool<convert_task, int>>(
"converter-pool",
[=](){return std::make_shared<ConverterWorker>(converter);},
[=](int&) {}
);
while (!converter->tasks.empty()) {
const convert_task& task = converter->tasks.front();
auto ptr = std::make_shared<convert_task>(task);
pool->enqueueJob(ptr);
converter->tasks.pop();
}
pool->setOnComplete([=]() {
converter->write();
onDone();
});
return pool;
}
void WorldConverter::convertRegion(fs::path file) {
void WorldConverter::convertRegion(fs::path file) const {
int x, z;
std::string name = file.stem().string();
if (!WorldFiles::parseRegionFilename(name, x, z)) {
std::cerr << "could not parse name " << name << std::endl;
if (!WorldRegions::parseRegionFilename(name, x, z)) {
logger.error() << "could not parse name " << name;
return;
}
std::cout << "converting region " << name << std::endl;
for (uint cz = 0; cz < REGION_SIZE; cz++) {
for (uint cx = 0; cx < REGION_SIZE; cx++) {
int gx = cx + x * REGION_SIZE;
int gz = cz + z * REGION_SIZE;
std::unique_ptr<ubyte[]> data (wfile->getChunk(gx, gz));
if (data == nullptr)
continue;
if (lut) {
Chunk::convert(data.get(), lut.get());
}
wfile->put(gx, gz, data.get());
logger.info() << "converting region " << name;
wfile->getRegions().processRegionVoxels(x, z, [=](ubyte* data) {
if (lut) {
Chunk::convert(data, lut.get());
}
}
return true;
});
}
void WorldConverter::convertPlayer(fs::path file) {
std::cout << "converting player " << file.u8string() << std::endl;
void WorldConverter::convertPlayer(fs::path file) const {
logger.info() << "converting player " << file.u8string();
auto map = files::read_json(file);
Player::convert(map.get(), lut.get());
files::write_json(file, map.get());
}
void WorldConverter::convertNext() {
if (!hasNext()) {
throw std::runtime_error("no more regions to convert");
}
convert_task task = tasks.front();
tasks.pop();
void WorldConverter::convert(convert_task task) const {
if (!fs::is_regular_file(task.file))
return;
switch (task.type) {
case convert_task_type::region:
convertRegion(task.file);
@@ -90,11 +122,51 @@ void WorldConverter::convertNext() {
}
}
void WorldConverter::convertNext() {
if (tasks.empty()) {
throw std::runtime_error("no more regions to convert");
}
convert_task task = tasks.front();
tasks.pop();
tasksDone++;
convert(task);
}
void WorldConverter::setOnComplete(runnable callback) {
this->onComplete = callback;
}
void WorldConverter::update() {
convertNext();
if (onComplete && tasks.empty()) {
onComplete();
}
}
void WorldConverter::terminate() {
tasks = {};
}
bool WorldConverter::isActive() const {
return !tasks.empty();
}
void WorldConverter::write() {
std::cout << "writing world" << std::endl;
logger.info() << "writing world";
wfile->write(nullptr, content);
}
uint WorldConverter::getTotalTasks() const {
return tasks.size();
void WorldConverter::waitForEnd() {
while (isActive()) {
update();
}
}
uint WorldConverter::getWorkTotal() const {
return tasks.size() + tasksDone;
}
uint WorldConverter::getWorkDone() const {
return tasksDone;
}
-45
View File
@@ -1,45 +0,0 @@
#ifndef FILES_WORLD_CONVERTER_H_
#define FILES_WORLD_CONVERTER_H_
#include <queue>
#include <memory>
#include <filesystem>
#include "../typedefs.h"
namespace fs = std::filesystem;
class Content;
class ContentLUT;
class WorldFiles;
enum class convert_task_type {
region, player
};
struct convert_task {
convert_task_type type;
fs::path file;
};
class WorldConverter {
WorldFiles* wfile;
std::shared_ptr<ContentLUT> const lut;
const Content* const content;
std::queue<convert_task> tasks;
void convertPlayer(fs::path file);
void convertRegion(fs::path file);
public:
WorldConverter(fs::path folder, const Content* content,
std::shared_ptr<ContentLUT> lut);
~WorldConverter();
bool hasNext() const;
void convertNext();
void write();
uint getTotalTasks() const;
};
#endif // FILES_WORLD_CONVERTER_H_
+66
View File
@@ -0,0 +1,66 @@
#ifndef FILES_WORLD_CONVERTER_HPP_
#define FILES_WORLD_CONVERTER_HPP_
#include <queue>
#include <memory>
#include <filesystem>
#include "../typedefs.hpp"
#include "../delegates.hpp"
#include "../interfaces/Task.hpp"
namespace fs = std::filesystem;
class Content;
class ContentLUT;
class WorldFiles;
enum class convert_task_type {
region, player
};
struct convert_task {
convert_task_type type;
fs::path file;
};
class WorldConverter : public Task {
std::unique_ptr<WorldFiles> wfile;
std::shared_ptr<ContentLUT> const lut;
const Content* const content;
std::queue<convert_task> tasks;
runnable onComplete;
uint tasksDone = 0;
void convertPlayer(fs::path file) const;
void convertRegion(fs::path file) const;
public:
WorldConverter(
fs::path folder,
const Content* content,
std::shared_ptr<ContentLUT> lut
);
~WorldConverter();
void convert(convert_task task) const;
void convertNext();
void setOnComplete(runnable callback);
void write();
void update() override;
void terminate() override;
bool isActive() const override;
void waitForEnd() override;
uint getWorkTotal() const override;
uint getWorkDone() const override;
static std::shared_ptr<Task> startTask(
fs::path folder,
const Content* content,
std::shared_ptr<ContentLUT> lut,
runnable onDone,
bool multithreading
);
};
#endif // FILES_WORLD_CONVERTER_HPP_
+46 -509
View File
@@ -1,26 +1,24 @@
#include "WorldFiles.h"
#include "WorldFiles.hpp"
#include "rle.h"
#include "../window/Camera.h"
#include "../content/Content.h"
#include "../objects/Player.h"
#include "../physics/Hitbox.h"
#include "../voxels/voxel.h"
#include "../voxels/Block.h"
#include "../voxels/Chunk.h"
#include "../typedefs.h"
#include "../maths/voxmaths.h"
#include "../world/World.h"
#include "../lighting/Lightmap.h"
#include "../coders/byte_utils.h"
#include "../util/data_io.h"
#include "../coders/json.h"
#include "../constants.h"
#include "../items/ItemDef.h"
#include "../items/Inventory.h"
#include "../data/dynamic.h"
#include "../coders/byte_utils.hpp"
#include "../coders/json.hpp"
#include "../constants.hpp"
#include "../content/Content.hpp"
#include "../core_defs.hpp"
#include "../data/dynamic.hpp"
#include "../items/Inventory.hpp"
#include "../items/ItemDef.hpp"
#include "../lighting/Lightmap.hpp"
#include "../maths/voxmaths.hpp"
#include "../objects/Player.hpp"
#include "../physics/Hitbox.hpp"
#include "../typedefs.hpp"
#include "../util/data_io.hpp"
#include "../voxels/Block.hpp"
#include "../voxels/Chunk.hpp"
#include "../voxels/voxel.hpp"
#include "../window/Camera.hpp"
#include "../world/World.hpp"
#include <cassert>
#include <iostream>
@@ -29,77 +27,18 @@
#include <sstream>
#include <cstring>
#define REGION_FORMAT_MAGIC ".VOXREG"
#define WORLD_FORMAT_MAGIC ".VOXWLD"
const size_t BUFFER_SIZE_UNKNOWN = -1;
regfile::regfile(fs::path filename) : file(filename) {
if (file.length() < REGION_HEADER_SIZE)
throw std::runtime_error("incomplete region file header");
char header[REGION_HEADER_SIZE];
file.read(header, REGION_HEADER_SIZE);
// avoid of use strcmp_s
if (std::string(header, strlen(REGION_FORMAT_MAGIC)) != REGION_FORMAT_MAGIC) {
throw std::runtime_error("invalid region file magic number");
}
version = header[8];
if (uint(version) > REGION_FORMAT_VERSION) {
throw illegal_region_format(
"region format "+std::to_string(version)+" is not supported");
}
WorldFiles::WorldFiles(fs::path directory) : directory(directory), regions(directory) {
}
WorldRegion::WorldRegion() {
chunksData = new ubyte*[REGION_CHUNKS_COUNT]{};
sizes = new uint32_t[REGION_CHUNKS_COUNT]{};
}
WorldRegion::~WorldRegion() {
for (uint i = 0; i < REGION_CHUNKS_COUNT; i++) {
delete[] chunksData[i];
}
delete[] sizes;
delete[] chunksData;
}
void WorldRegion::setUnsaved(bool unsaved) {
this->unsaved = unsaved;
}
bool WorldRegion::isUnsaved() const {
return unsaved;
}
ubyte** WorldRegion::getChunks() const {
return chunksData;
}
uint32_t* WorldRegion::getSizes() const {
return sizes;
}
void WorldRegion::put(uint x, uint z, ubyte* data, uint32_t size) {
size_t chunk_index = z * REGION_SIZE + x;
delete[] chunksData[chunk_index];
chunksData[chunk_index] = data;
sizes[chunk_index] = size;
}
ubyte* WorldRegion::getChunkData(uint x, uint z) {
return chunksData[z * REGION_SIZE + x];
}
uint WorldRegion::getChunkDataSize(uint x, uint z) {
return sizes[z * REGION_SIZE + x];
}
WorldFiles::WorldFiles(fs::path directory, const DebugSettings& settings)
: directory(directory),
generatorTestMode(settings.generatorTestMode),
doWriteLights(settings.doWriteLights)
WorldFiles::WorldFiles(fs::path directory, const DebugSettings& settings)
: WorldFiles(directory)
{
compressionBuffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN * 2);
generatorTestMode = settings.generatorTestMode.get();
doWriteLights = settings.doWriteLights.get();
regions.generatorTestMode = generatorTestMode;
regions.doWriteLights = doWriteLights;
}
WorldFiles::~WorldFiles() {
@@ -110,172 +49,6 @@ void WorldFiles::createDirectories() {
fs::create_directories(directory / fs::path("content"));
}
WorldRegion* WorldFiles::getRegion(regionsmap& regions, int x, int z) {
auto found = regions.find(glm::ivec2(x, z));
if (found == regions.end())
return nullptr;
return found->second.get();
}
WorldRegion* WorldFiles::getOrCreateRegion(regionsmap& regions, int x, int z) {
WorldRegion* region = getRegion(regions, x, z);
if (region == nullptr) {
region = new WorldRegion();
regions[glm::ivec2(x, z)].reset(region);
}
return region;
}
ubyte* WorldFiles::compress(const ubyte* src, size_t srclen, size_t& len) {
ubyte* buffer = this->compressionBuffer.get();
len = extrle::encode(src, srclen, buffer);
ubyte* data = new ubyte[len];
for (size_t i = 0; i < len; i++) {
data[i] = buffer[i];
}
return data;
}
ubyte* WorldFiles::decompress(const ubyte* src, size_t srclen, size_t dstlen) {
ubyte* decompressed = new ubyte[dstlen];
extrle::decode(src, srclen, decompressed);
return decompressed;
}
int WorldFiles::getVoxelRegionVersion(int x, int z) {
regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), getRegionsFolder());
if (rf == nullptr) {
return 0;
}
return rf->version;
}
int WorldFiles::getVoxelRegionsVersion() {
fs::path regionsFolder = getRegionsFolder();
if (!fs::is_directory(regionsFolder)) {
return REGION_FORMAT_VERSION;
}
for (auto file : fs::directory_iterator(regionsFolder)) {
int x;
int z;
if (!parseRegionFilename(file.path().stem().string(), x, z)) {
continue;
}
regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), regionsFolder);
return rf->version;
}
return REGION_FORMAT_VERSION;
}
/// @brief Compress and store chunk voxels data in region
/// @param x chunk.x
/// @param z chunk.z
void WorldFiles::put(int x, int z, const ubyte* voxelData) {
int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE);
/* Writing Voxels */ {
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
region->setUnsaved(true);
size_t compressedSize;
ubyte* data = compress(voxelData, CHUNK_DATA_LEN, compressedSize);
region->put(localX, localZ, data, compressedSize);
}
}
/// @brief Store chunk (voxels and lights) in region (existing or new)
void WorldFiles::put(Chunk* chunk){
assert(chunk != nullptr);
int regionX = floordiv(chunk->x, REGION_SIZE);
int regionZ = floordiv(chunk->z, REGION_SIZE);
int localX = chunk->x - (regionX * REGION_SIZE);
int localZ = chunk->z - (regionZ * REGION_SIZE);
/* Writing voxels */ {
size_t compressedSize;
std::unique_ptr<ubyte[]> chunk_data (chunk->encode());
ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize);
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
region->setUnsaved(true);
region->put(localX, localZ, data, compressedSize);
}
// Writing lights cache
if (doWriteLights && chunk->isLighted()) {
size_t compressedSize;
std::unique_ptr<ubyte[]> light_data (chunk->lightmap.encode());
ubyte* data = compress(light_data.get(), LIGHTMAP_DATA_LEN, compressedSize);
WorldRegion* region = getOrCreateRegion(lights, regionX, regionZ);
region->setUnsaved(true);
region->put(localX, localZ, data, compressedSize);
}
// Writing block inventories
if (!chunk->inventories.empty()){
auto& inventories = chunk->inventories;
ByteBuilder builder;
builder.putInt32(inventories.size());
for (auto& entry : inventories) {
builder.putInt32(entry.first);
auto map = entry.second->serialize();
auto bytes = json::to_binary(map.get(), true);
builder.putInt32(bytes.size());
builder.put(bytes.data(), bytes.size());
}
WorldRegion* region = getOrCreateRegion(storages, regionX, regionZ);
region->setUnsaved(true);
auto datavec = builder.data();
uint datasize = builder.size();
auto data = std::make_unique<ubyte[]>(datasize);
for (uint i = 0; i < datasize; i++) {
data[i] = datavec[i];
}
region->put(localX, localZ, data.release(), datasize);
}
}
fs::path WorldFiles::getRegionsFolder() const {
return directory/fs::path("regions");
}
fs::path WorldFiles::getLightsFolder() const {
return directory/fs::path("lights");
}
fs::path WorldFiles::getInventoriesFolder() const {
return directory/fs::path("inventories");
}
fs::path WorldFiles::getRegionFilename(int x, int z) const {
return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin");
}
/// @brief Extract X and Z from 'X_Z.bin' region file name.
/// @param name source region file name
/// @param x parsed X destination
/// @param z parsed Z destination
/// @return false if std::invalid_argument or std::out_of_range occurred
bool WorldFiles::parseRegionFilename(const std::string& name, int& x, int& z) {
size_t sep = name.find('_');
if (sep == std::string::npos || sep == 0 || sep == name.length()-1)
return false;
try {
x = std::stoi(name.substr(0, sep));
z = std::stoi(name.substr(sep+1));
} catch (std::invalid_argument& err) {
return false;
} catch (std::out_of_range& err) {
return false;
}
return true;
}
fs::path WorldFiles::getPlayerFile() const {
return directory/fs::path("player.json");
}
@@ -292,229 +65,23 @@ fs::path WorldFiles::getPacksFile() const {
return directory/fs::path("packs.list");
}
ubyte* WorldFiles::getChunk(int x, int z){
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true);
}
/// @brief Get cached lights for chunk at x,z
/// @return lights data or nullptr
light_t* WorldFiles::getLights(int x, int z) {
std::unique_ptr<ubyte[]> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true));
if (data == nullptr)
return nullptr;
return Lightmap::decode(data.get());
}
chunk_inventories_map WorldFiles::fetchInventories(int x, int z) {
chunk_inventories_map inventories;
const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false);
if (data == nullptr)
return inventories;
ByteReader reader(data, BUFFER_SIZE_UNKNOWN);
int count = reader.getInt32();
for (int i = 0; i < count; i++) {
uint index = reader.getInt32();
uint size = reader.getInt32();
auto map = json::from_binary(reader.pointer(), size);
reader.skip(size);
auto inv = std::make_shared<Inventory>(0, 0);
inv->deserialize(map.get());
inventories[index] = inv;
}
return inventories;
}
ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
int x, int z, int layer, bool compression) {
int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE);
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
ubyte* data = region->getChunkData(localX, localZ);
if (data == nullptr) {
uint32_t size;
data = readChunkData(x, z, size, folder, layer);
if (data != nullptr) {
region->put(localX, localZ, data, size);
}
}
if (data != nullptr) {
size_t size = region->getChunkDataSize(localX, localZ);
if (compression) {
return decompress(data, size, CHUNK_DATA_LEN);
}
return data;
}
return nullptr;
}
regfile* WorldFiles::getRegFile(glm::ivec3 coord, const fs::path& folder) {
const auto found = openRegFiles.find(coord);
if (found != openRegFiles.end()) {
return found->second.get();
}
if (openRegFiles.size() == MAX_OPEN_REGION_FILES) {
// [todo] replace with closing the most unused region
auto iter = std::next(openRegFiles.begin(), rand() % openRegFiles.size());
openRegFiles.erase(iter);
}
fs::path filename = folder / getRegionFilename(coord[0], coord[1]);
if (!fs::is_regular_file(filename)) {
return nullptr;
}
openRegFiles[coord] = std::make_unique<regfile>(filename);
return openRegFiles[coord].get();
}
ubyte* WorldFiles::readChunkData(int x,
int z,
uint32_t& length,
fs::path folder,
int layer){
if (generatorTestMode)
return nullptr;
int regionX = floordiv(x, REGION_SIZE);
int regionZ = floordiv(z, REGION_SIZE);
int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE);
int chunkIndex = localZ * REGION_SIZE + localX;
glm::ivec3 coord(regionX, regionZ, layer);
regfile* rfile = WorldFiles::getRegFile(coord, folder);
if (rfile == nullptr) {
return nullptr;
}
files::rafile& file = rfile->file;
size_t file_size = file.length();
size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4;
uint32_t offset;
file.seekg(table_offset + chunkIndex * 4);
file.read((char*)(&offset), 4);
offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
if (offset == 0){
return nullptr;
}
file.seekg(offset);
file.read((char*)(&offset), 4);
length = dataio::read_int32_big((const ubyte*)(&offset), 0);
ubyte* data = new ubyte[length]{};
file.read((char*)data, length);
return data;
}
/// @brief Read missing chunks data (null pointers) from region file
/// @param layer used as third part of openRegFiles map key
/// (see REGION_LAYER_* constants)
void WorldFiles::fetchChunks(WorldRegion* region, int x, int z, fs::path folder, int layer) {
ubyte** chunks = region->getChunks();
uint32_t* sizes = region->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE;
int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE;
if (chunks[i] == nullptr) {
chunks[i] = readChunkData(chunk_x, chunk_z, sizes[i], folder, layer);
}
}
}
/// @brief Write or rewrite region file
/// @param x region X
/// @param z region Z
/// @param layer used as third part of openRegFiles map key
/// (see REGION_LAYER_* constants)
void WorldFiles::writeRegion(int x, int z, WorldRegion* entry, fs::path folder, int layer){
fs::path filename = folder/getRegionFilename(x, z);
glm::ivec3 regcoord(x, z, layer);
if (getRegFile(regcoord, folder)) {
fetchChunks(entry, x, z, folder, layer);
openRegFiles.erase(regcoord);
}
char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC;
header[8] = REGION_FORMAT_VERSION;
header[9] = 0; // flags
std::ofstream file(filename, std::ios::out | std::ios::binary);
file.write(header, REGION_HEADER_SIZE);
size_t offset = REGION_HEADER_SIZE;
char intbuf[4]{};
uint offsets[REGION_CHUNKS_COUNT]{};
ubyte** region = entry->getChunks();
uint32_t* sizes = entry->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
ubyte* chunk = region[i];
if (chunk == nullptr){
offsets[i] = 0;
} else {
offsets[i] = offset;
size_t compressedSize = sizes[i];
dataio::write_int32_big(compressedSize, (ubyte*)intbuf, 0);
offset += 4 + compressedSize;
file.write(intbuf, 4);
file.write((const char*)chunk, compressedSize);
}
}
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
dataio::write_int32_big(offsets[i], (ubyte*)intbuf, 0);
file.write(intbuf, 4);
}
}
void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int layer) {
for (auto& it : regions){
WorldRegion* region = it.second.get();
if (region->getChunks() == nullptr || !region->isUnsaved())
continue;
glm::ivec2 key = it.first;
writeRegion(key[0], key[1], region, folder, layer);
}
}
void WorldFiles::write(const World* world, const Content* content) {
fs::path regionsFolder = getRegionsFolder();
fs::path lightsFolder = getLightsFolder();
fs::path inventoriesFolder = getInventoriesFolder();
fs::create_directories(regionsFolder);
fs::create_directories(inventoriesFolder);
fs::create_directories(lightsFolder);
if (world) {
writeWorldInfo(world);
writePacks(world);
if (!fs::exists(getPacksFile())) {
writePacks(world->getPacks());
}
}
if (generatorTestMode) {
return;
}
writeIndices(content->getIndices());
writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS);
writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS);
writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES);
regions.write();
}
void WorldFiles::writePacks(const World* world) {
void WorldFiles::writePacks(const std::vector<ContentPack>& packs) {
auto packsFile = getPacksFile();
if (fs::is_regular_file(packsFile)) {
return;
}
const auto& packs = world->getPacks();
std::stringstream ss;
ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) {
@@ -559,56 +126,15 @@ bool WorldFiles::readWorldInfo(World* world) {
return true;
}
void WorldFiles::addPack(const World* world, const std::string& id) {
fs::path file = getPacksFile();
if (!fs::is_regular_file(file)) {
if (!fs::is_directory(directory)) {
fs::create_directories(directory);
}
writePacks(world);
}
auto packs = files::read_list(file);
packs.push_back(id);
std::stringstream ss;
ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) {
ss << pack << "\n";
}
files::write_string(file, ss.str());
}
void WorldFiles::removePack(const World* world, const std::string& id) {
fs::path file = getPacksFile();
if (!fs::is_regular_file(file)) {
if (!fs::is_directory(directory)) {
fs::create_directories(directory);
}
writePacks(world);
}
auto packs = files::read_list(file);
auto found = std::find(packs.begin(), packs.end(), id);
if (found != packs.end()) {
packs.erase(found);
}
std::stringstream ss;
ss << "# autogenerated; do not modify\n";
for (const auto& pack : packs) {
ss << pack << "\n";
}
files::write_string(file, ss.str());
// erase invalid indices
static void erase_pack_indices(dynamic::Map* root, const std::string& id) {
auto prefix = id+":";
auto root = files::read_json(getIndicesFile());
auto blocks = root->list("blocks");
for (uint i = 0; i < blocks->size(); i++) {
auto name = blocks->str(i);
if (name.find(prefix) != 0)
continue;
auto value = blocks->getValueWriteable(i);
*value->value.str = "core:air";
*value = CORE_AIR;
}
auto items = root->list("items");
@@ -617,7 +143,18 @@ void WorldFiles::removePack(const World* world, const std::string& id) {
if (name.find(prefix) != 0)
continue;
auto value = items->getValueWriteable(i);
*value->value.str = "core:empty";
*value = CORE_EMPTY;
}
}
void WorldFiles::removeIndices(const std::vector<std::string>& packs) {
auto root = files::read_json(getIndicesFile());
for (const auto& id : packs) {
erase_pack_indices(root.get(), id);
}
files::write_json(getIndicesFile(), root.get());
}
fs::path WorldFiles::getFolder() const {
return directory;
}
-166
View File
@@ -1,166 +0,0 @@
#ifndef FILES_WORLDFILES_H_
#define FILES_WORLDFILES_H_
#include <map>
#include <string>
#include <memory>
#include <unordered_map>
#include <filesystem>
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtx/hash.hpp"
#include "files.h"
#include "../typedefs.h"
#include "../settings.h"
#include "../voxels/Chunk.h"
inline constexpr uint REGION_HEADER_SIZE = 10;
inline constexpr uint REGION_LAYER_VOXELS = 0;
inline constexpr uint REGION_LAYER_LIGHTS = 1;
inline constexpr uint REGION_LAYER_INVENTORIES = 2;
inline constexpr uint REGION_SIZE_BIT = 5;
inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
inline constexpr uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE));
inline constexpr uint REGION_FORMAT_VERSION = 2;
inline constexpr uint WORLD_FORMAT_VERSION = 1;
inline constexpr uint MAX_OPEN_REGION_FILES = 16;
class Player;
class Content;
class ContentIndices;
class World;
namespace fs = std::filesystem;
class illegal_region_format : public std::runtime_error {
public:
illegal_region_format(const std::string& message)
: std::runtime_error(message) {}
};
class WorldRegion {
ubyte** chunksData;
uint32_t* sizes;
bool unsaved = false;
public:
WorldRegion();
~WorldRegion();
void put(uint x, uint z, ubyte* data, uint32_t size);
ubyte* getChunkData(uint x, uint z);
uint getChunkDataSize(uint x, uint z);
void setUnsaved(bool unsaved);
bool isUnsaved() const;
ubyte** getChunks() const;
uint32_t* getSizes() const;
};
struct regfile {
files::rafile file;
int version;
regfile(fs::path filename);
};
typedef std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>> regionsmap;
class WorldFiles {
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
void writeWorldInfo(const World* world);
fs::path getRegionFilename(int x, int y) const;
fs::path getWorldFile() const;
fs::path getIndicesFile() const;
fs::path getPacksFile() const;
WorldRegion* getRegion(regionsmap& regions, int x, int z);
WorldRegion* getOrCreateRegion(regionsmap& regions, int x, int z);
/// @brief Compress buffer with extrle
/// @param src source buffer
/// @param srclen length of the source buffer
/// @param len (out argument) length of result buffer
/// @return compressed bytes array
ubyte* compress(const ubyte* src, size_t srclen, size_t& len);
/// @brief Decompress buffer with extrle
/// @param src compressed buffer
/// @param srclen length of compressed buffer
/// @param dstlen max expected length of source buffer
/// @return decompressed bytes array
ubyte* decompress(const ubyte* src, size_t srclen, size_t dstlen);
ubyte* readChunkData(int x, int y, uint32_t& length, fs::path folder, int layer);
void fetchChunks(WorldRegion* region, int x, int y, fs::path folder, int layer);
void writeRegions(regionsmap& regions, const fs::path& folder, int layer);
ubyte* getData(regionsmap& regions, const fs::path& folder, int x, int z, int layer, bool compression);
regfile* getRegFile(glm::ivec3 coord, const fs::path& folder);
fs::path getLightsFolder() const;
fs::path getInventoriesFolder() const;
public:
static bool parseRegionFilename(const std::string& name, int& x, int& y);
fs::path getRegionsFolder() const;
fs::path getPlayerFile() const;
regionsmap regions;
regionsmap storages;
regionsmap lights;
fs::path directory;
std::unique_ptr<ubyte[]> compressionBuffer;
bool generatorTestMode;
bool doWriteLights;
WorldFiles(fs::path directory, const DebugSettings& settings);
~WorldFiles();
void createDirectories();
void put(Chunk* chunk);
void put(int x, int z, const ubyte* voxelData);
int getVoxelRegionVersion(int x, int z);
int getVoxelRegionsVersion();
ubyte* getChunk(int x, int z);
light_t* getLights(int x, int z);
chunk_inventories_map fetchInventories(int x, int z);
bool readWorldInfo(World* world);
void writeRegion(int x, int y, WorldRegion* entry, fs::path file, int layer);
/// @brief Write all unsaved data to world files
/// @param world target world
/// @param content world content
void write(const World* world, const Content* content);
void writePacks(const World* world);
void writeIndices(const ContentIndices* indices);
/// @brief Append pack to the packs list without duplicate check and
/// dependencies resolve
/// @param world target world
/// @param id pack id
void addPack(const World* world, const std::string& id);
/// @brief Remove pack from the list (does not remove indices)
/// @param world target world
/// @param id pack id
void removePack(const World* world, const std::string& id);
static const inline std::string WORLD_FILE = "world.json";
};
#endif /* FILES_WORLDFILES_H_ */
+76
View File
@@ -0,0 +1,76 @@
#ifndef FILES_WORLD_FILES_HPP_
#define FILES_WORLD_FILES_HPP_
#include "WorldRegions.hpp"
#include "files.hpp"
#include "../typedefs.hpp"
#include "../settings.hpp"
#include "../content/ContentPack.hpp"
#include "../voxels/Chunk.hpp"
#include <vector>
#include <string>
#include <memory>
#include <filesystem>
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtx/hash.hpp"
inline constexpr uint WORLD_FORMAT_VERSION = 1;
class Player;
class Content;
class ContentIndices;
class World;
namespace fs = std::filesystem;
class WorldFiles {
fs::path directory;
WorldRegions regions;
bool generatorTestMode = false;
bool doWriteLights = true;
fs::path getWorldFile() const;
fs::path getIndicesFile() const;
fs::path getPacksFile() const;
void writeWorldInfo(const World* world);
void writeIndices(const ContentIndices* indices);
public:
WorldFiles(fs::path directory);
WorldFiles(fs::path directory, const DebugSettings& settings);
~WorldFiles();
fs::path getPlayerFile() const;
void createDirectories();
bool readWorldInfo(World* world);
/// @brief Write all unsaved data to world files
/// @param world target world
/// @param content world content
void write(const World* world, const Content* content);
void writePacks(const std::vector<ContentPack>& packs);
void removeIndices(const std::vector<std::string>& packs);
/// @return world folder
fs::path getFolder() const;
static const inline std::string WORLD_FILE = "world.json";
WorldRegions& getRegions() {
return regions;
}
bool doesWriteLights() const {
return doWriteLights;
}
};
#endif // FILES_WORLD_FILES_HPP_
+479
View File
@@ -0,0 +1,479 @@
#include "WorldRegions.hpp"
#include "../coders/byte_utils.hpp"
#include "../coders/rle.hpp"
#include "../data/dynamic.hpp"
#include "../items/Inventory.hpp"
#include "../maths/voxmaths.hpp"
#include "../util/data_io.hpp"
#include <cstring>
#define REGION_FORMAT_MAGIC ".VOXREG"
regfile::regfile(fs::path filename) : file(filename) {
if (file.length() < REGION_HEADER_SIZE)
throw std::runtime_error("incomplete region file header");
char header[REGION_HEADER_SIZE];
file.read(header, REGION_HEADER_SIZE);
// avoid of use strcmp_s
if (std::string(header, strlen(REGION_FORMAT_MAGIC)) != REGION_FORMAT_MAGIC) {
throw std::runtime_error("invalid region file magic number");
}
version = header[8];
if (uint(version) > REGION_FORMAT_VERSION) {
throw illegal_region_format(
"region format "+std::to_string(version)+" is not supported"
);
}
}
std::unique_ptr<ubyte[]> regfile::read(int index, uint32_t& length) {
size_t file_size = file.length();
size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4;
uint32_t offset;
file.seekg(table_offset + index * 4);
file.read((char*)(&offset), 4);
offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
if (offset == 0){
return nullptr;
}
file.seekg(offset);
file.read((char*)(&offset), 4);
length = dataio::read_int32_big((const ubyte*)(&offset), 0);
auto data = std::make_unique<ubyte[]>(length);
file.read((char*)data.get(), length);
return data;
}
WorldRegion::WorldRegion() {
chunksData = new ubyte*[REGION_CHUNKS_COUNT]{};
sizes = new uint32_t[REGION_CHUNKS_COUNT]{};
}
WorldRegion::~WorldRegion() {
for (uint i = 0; i < REGION_CHUNKS_COUNT; i++) {
delete[] chunksData[i];
}
delete[] sizes;
delete[] chunksData;
}
void WorldRegion::setUnsaved(bool unsaved) {
this->unsaved = unsaved;
}
bool WorldRegion::isUnsaved() const {
return unsaved;
}
ubyte** WorldRegion::getChunks() const {
return chunksData;
}
uint32_t* WorldRegion::getSizes() const {
return sizes;
}
void WorldRegion::put(uint x, uint z, ubyte* data, uint32_t size) {
size_t chunk_index = z * REGION_SIZE + x;
delete[] chunksData[chunk_index];
chunksData[chunk_index] = data;
sizes[chunk_index] = size;
}
ubyte* WorldRegion::getChunkData(uint x, uint z) {
return chunksData[z * REGION_SIZE + x];
}
uint WorldRegion::getChunkDataSize(uint x, uint z) {
return sizes[z * REGION_SIZE + x];
}
WorldRegions::WorldRegions(fs::path directory) : directory(directory) {
for (uint i = 0; i < sizeof(layers)/sizeof(RegionsLayer); i++) {
layers[i].layer = i;
}
layers[REGION_LAYER_VOXELS].folder = directory/fs::path("regions");
layers[REGION_LAYER_LIGHTS].folder = directory/fs::path("lights");
layers[REGION_LAYER_INVENTORIES].folder = directory/fs::path("inventories");
}
WorldRegions::~WorldRegions() {
}
WorldRegion* WorldRegions::getRegion(int x, int z, int layer) {
RegionsLayer& regions = layers[layer];
std::lock_guard lock(regions.mutex);
auto found = regions.regions.find(glm::ivec2(x, z));
if (found == regions.regions.end())
return nullptr;
return found->second.get();
}
WorldRegion* WorldRegions::getOrCreateRegion(int x, int z, int layer) {
WorldRegion* region = getRegion(x, z, layer);
if (region == nullptr) {
RegionsLayer& regions = layers[layer];
std::lock_guard lock(regions.mutex);
region = new WorldRegion();
regions.regions[glm::ivec2(x, z)].reset(region);
}
return region;
}
std::unique_ptr<ubyte[]> WorldRegions::compress(const ubyte* src, size_t srclen, size_t& len) {
auto buffer = bufferPool.get();
ubyte* bytes = buffer.get();
len = extrle::encode(src, srclen, bytes);
auto data = std::make_unique<ubyte[]>(len);
for (size_t i = 0; i < len; i++) {
data[i] = bytes[i];
}
return data;
}
std::unique_ptr<ubyte[]> WorldRegions::decompress(const ubyte* src, size_t srclen, size_t dstlen) {
auto decompressed = std::make_unique<ubyte[]>(dstlen);
extrle::decode(src, srclen, decompressed.get());
return decompressed;
}
inline void calc_reg_coords(
int x, int z, int& regionX, int& regionZ, int& localX, int& localZ
) {
regionX = floordiv(x, REGION_SIZE);
regionZ = floordiv(z, REGION_SIZE);
localX = x - (regionX * REGION_SIZE);
localZ = z - (regionZ * REGION_SIZE);
}
std::unique_ptr<ubyte[]> WorldRegions::readChunkData(
int x,
int z,
uint32_t& length,
regfile* rfile
){
int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
int chunkIndex = localZ * REGION_SIZE + localX;
return rfile->read(chunkIndex, length);
}
/// @brief Read missing chunks data (null pointers) from region file
void WorldRegions::fetchChunks(WorldRegion* region, int x, int z, regfile* file) {
ubyte** chunks = region->getChunks();
uint32_t* sizes = region->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE;
int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE;
if (chunks[i] == nullptr) {
chunks[i] = readChunkData(chunk_x, chunk_z, sizes[i], file).release();
}
}
}
ubyte* WorldRegions::getData(
int x, int z, int layer,
uint32_t& size
) {
if (generatorTestMode) {
return nullptr;
}
int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
WorldRegion* region = getOrCreateRegion(regionX, regionZ, layer);
ubyte* data = region->getChunkData(localX, localZ);
if (data == nullptr) {
auto regfile = getRegFile(glm::ivec3(regionX, regionZ, layer));
if (regfile != nullptr) {
data = readChunkData(x, z, size, regfile.get()).release();
}
if (data != nullptr) {
region->put(localX, localZ, data, size);
}
}
if (data != nullptr) {
size = region->getChunkDataSize(localX, localZ);
return data;
}
return nullptr;
}
std::shared_ptr<regfile> WorldRegions::useRegFile(glm::ivec3 coord) {
auto* file = openRegFiles[coord].get();
file->inUse = true;
return std::shared_ptr<regfile>(file, [this](regfile* ptr) {
ptr->inUse = false;
regFilesCv.notify_one();
});
}
void WorldRegions::closeRegFile(glm::ivec3 coord) {
openRegFiles.erase(coord);
regFilesCv.notify_one();
}
// Marks regfile as used and unmarks when shared_ptr dies
std::shared_ptr<regfile> WorldRegions::getRegFile(glm::ivec3 coord, bool create) {
{
std::lock_guard lock(regFilesMutex);
const auto found = openRegFiles.find(coord);
if (found != openRegFiles.end()) {
if (found->second->inUse) {
throw std::runtime_error("regfile is currently in use");
}
return useRegFile(found->first);
}
}
if (create) {
return createRegFile(coord);
}
return nullptr;
}
std::shared_ptr<regfile> WorldRegions::createRegFile(glm::ivec3 coord) {
fs::path file = layers[coord[2]].folder/getRegionFilename(coord[0], coord[1]);
if (!fs::exists(file)) {
return nullptr;
}
if (openRegFiles.size() == MAX_OPEN_REGION_FILES) {
std::unique_lock lock(regFilesMutex);
while (true) {
bool closed = false;
// FIXME: bad choosing algorithm
for (auto& entry : openRegFiles) {
if (!entry.second->inUse) {
closeRegFile(entry.first);
closed = true;
break;
}
}
if (closed) {
break;
}
// notified when any regfile gets out of use or closed
regFilesCv.wait(lock);
}
openRegFiles[coord] = std::make_unique<regfile>(file);
return useRegFile(coord);
} else {
std::lock_guard lock(regFilesMutex);
openRegFiles[coord] = std::make_unique<regfile>(file);
return useRegFile(coord);
}
}
fs::path WorldRegions::getRegionFilename(int x, int z) const {
return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin");
}
void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry){
fs::path filename = layers[layer].folder/getRegionFilename(x, z);
glm::ivec3 regcoord(x, z, layer);
if (auto regfile = getRegFile(regcoord, false)) {
fetchChunks(entry, x, z, regfile.get());
std::lock_guard lock(regFilesMutex);
closeRegFile(regcoord);
}
char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC;
header[8] = REGION_FORMAT_VERSION;
header[9] = 0; // flags
std::ofstream file(filename, std::ios::out | std::ios::binary);
file.write(header, REGION_HEADER_SIZE);
size_t offset = REGION_HEADER_SIZE;
char intbuf[4]{};
uint offsets[REGION_CHUNKS_COUNT]{};
ubyte** region = entry->getChunks();
uint32_t* sizes = entry->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
ubyte* chunk = region[i];
if (chunk == nullptr){
offsets[i] = 0;
} else {
offsets[i] = offset;
size_t compressedSize = sizes[i];
dataio::write_int32_big(compressedSize, (ubyte*)intbuf, 0);
offset += 4 + compressedSize;
file.write(intbuf, 4);
file.write((const char*)chunk, compressedSize);
}
}
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
dataio::write_int32_big(offsets[i], (ubyte*)intbuf, 0);
file.write(intbuf, 4);
}
}
void WorldRegions::writeRegions(int layer) {
for (auto& it : layers[layer].regions){
WorldRegion* region = it.second.get();
if (region->getChunks() == nullptr || !region->isUnsaved())
continue;
glm::ivec2 key = it.first;
writeRegion(key[0], key[1], layer, region);
}
}
void WorldRegions::put(int x, int z, int layer, std::unique_ptr<ubyte[]> data, size_t size, bool rle) {
if (rle) {
size_t compressedSize;
auto compressed = compress(data.get(), size, compressedSize);
put(x, z, layer, std::move(compressed), compressedSize, false);
return;
}
int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
WorldRegion* region = getOrCreateRegion(regionX, regionZ, layer);
region->setUnsaved(true);
region->put(localX, localZ, data.release(), size);
}
static std::unique_ptr<ubyte[]> write_inventories(Chunk* chunk, uint& datasize) {
auto& inventories = chunk->inventories;
ByteBuilder builder;
builder.putInt32(inventories.size());
for (auto& entry : inventories) {
builder.putInt32(entry.first);
auto map = entry.second->serialize();
auto bytes = json::to_binary(map.get(), true);
builder.putInt32(bytes.size());
builder.put(bytes.data(), bytes.size());
}
auto datavec = builder.data();
datasize = builder.size();
auto data = std::make_unique<ubyte[]>(datasize);
for (uint i = 0; i < datasize; i++) {
data[i] = datavec[i];
}
return data;
}
/// @brief Store chunk (voxels and lights) in region (existing or new)
void WorldRegions::put(Chunk* chunk){
assert(chunk != nullptr);
int regionX, regionZ, localX, localZ;
calc_reg_coords(chunk->x, chunk->z, regionX, regionZ, localX, localZ);
put(chunk->x, chunk->z, REGION_LAYER_VOXELS,
chunk->encode(), CHUNK_DATA_LEN, true);
// Writing lights cache
if (doWriteLights && chunk->isLighted()) {
put(chunk->x, chunk->z, REGION_LAYER_LIGHTS,
chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true);
}
// Writing block inventories
if (!chunk->inventories.empty()){
uint datasize;
auto data = write_inventories(chunk, datasize);
put(chunk->x, chunk->z, REGION_LAYER_INVENTORIES,
std::move(data), datasize, false);
}
}
std::unique_ptr<ubyte[]> WorldRegions::getChunk(int x, int z){
uint32_t size;
auto* data = getData(x, z, REGION_LAYER_VOXELS, size);
if (data == nullptr) {
return nullptr;
}
return decompress(data, size, CHUNK_DATA_LEN);
}
/// @brief Get cached lights for chunk at x,z
/// @return lights data or nullptr
std::unique_ptr<light_t[]> WorldRegions::getLights(int x, int z) {
uint32_t size;
auto* bytes = getData(x, z, REGION_LAYER_LIGHTS, size);
if (bytes == nullptr)
return nullptr;
auto data = decompress(bytes, size, LIGHTMAP_DATA_LEN);
return Lightmap::decode(data.get());
}
chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
chunk_inventories_map inventories;
uint32_t bytesSize;
const ubyte* data = getData(x, z, REGION_LAYER_INVENTORIES, bytesSize);
if (data == nullptr)
return inventories;
ByteReader reader(data, bytesSize);
int count = reader.getInt32();
for (int i = 0; i < count; i++) {
uint index = reader.getInt32();
uint size = reader.getInt32();
auto map = json::from_binary(reader.pointer(), size);
reader.skip(size);
auto inv = std::make_shared<Inventory>(0, 0);
inv->deserialize(map.get());
inventories[index] = inv;
}
return inventories;
}
void WorldRegions::processRegionVoxels(int x, int z, regionproc func) {
if (getRegion(x, z, REGION_LAYER_VOXELS)) {
throw std::runtime_error("not implemented for in-memory regions");
}
auto regfile = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS));
if (regfile == nullptr) {
throw std::runtime_error("could not open region file");
}
for (uint cz = 0; cz < REGION_SIZE; cz++) {
for (uint cx = 0; cx < REGION_SIZE; cx++) {
int gx = cx + x * REGION_SIZE;
int gz = cz + z * REGION_SIZE;
uint32_t length;
auto data = readChunkData(gx, gz, length, regfile.get());
if (data == nullptr)
continue;
data = decompress(data.get(), length, CHUNK_DATA_LEN);
if (func(data.get())) {
put(gx, gz, REGION_LAYER_VOXELS, std::move(data), CHUNK_DATA_LEN, true);
}
}
}
}
fs::path WorldRegions::getRegionsFolder(int layer) const {
return layers[layer].folder;
}
void WorldRegions::write() {
for (auto& layer : layers) {
fs::create_directories(layer.folder);
writeRegions(layer.layer);
}
}
bool WorldRegions::parseRegionFilename(const std::string& name, int& x, int& z) {
size_t sep = name.find('_');
if (sep == std::string::npos || sep == 0 || sep == name.length()-1)
return false;
try {
x = std::stoi(name.substr(0, sep));
z = std::stoi(name.substr(sep+1));
} catch (std::invalid_argument& err) {
return false;
} catch (std::out_of_range& err) {
return false;
}
return true;
}
+165
View File
@@ -0,0 +1,165 @@
#ifndef FILES_WORLD_REGIONS_HPP_
#define FILES_WORLD_REGIONS_HPP_
#include "files.hpp"
#include "../typedefs.hpp"
#include "../util/BufferPool.hpp"
#include "../voxels/Chunk.hpp"
#include <mutex>
#include <memory>
#include <functional>
#include <filesystem>
#include <unordered_map>
#include <condition_variable>
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtx/hash.hpp"
namespace fs = std::filesystem;
inline constexpr uint REGION_HEADER_SIZE = 10;
inline constexpr uint REGION_LAYER_VOXELS = 0;
inline constexpr uint REGION_LAYER_LIGHTS = 1;
inline constexpr uint REGION_LAYER_INVENTORIES = 2;
inline constexpr uint REGION_SIZE_BIT = 5;
inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
inline constexpr uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE));
inline constexpr uint REGION_FORMAT_VERSION = 2;
inline constexpr uint MAX_OPEN_REGION_FILES = 16;
class illegal_region_format : public std::runtime_error {
public:
illegal_region_format(const std::string& message)
: std::runtime_error(message) {}
};
class WorldRegion {
ubyte** chunksData;
uint32_t* sizes;
bool unsaved = false;
public:
WorldRegion();
~WorldRegion();
void put(uint x, uint z, ubyte* data, uint32_t size);
ubyte* getChunkData(uint x, uint z);
uint getChunkDataSize(uint x, uint z);
void setUnsaved(bool unsaved);
bool isUnsaved() const;
ubyte** getChunks() const;
uint32_t* getSizes() const;
};
struct regfile {
files::rafile file;
int version;
bool inUse = false;
regfile(fs::path filename);
regfile(const regfile&) = delete;
std::unique_ptr<ubyte[]> read(int index, uint32_t& length);
};
using regionsmap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>;
using regionproc = std::function<bool(ubyte*)>;
struct RegionsLayer {
int layer;
fs::path folder;
regionsmap regions;
std::mutex mutex;
};
class WorldRegions {
fs::path directory;
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
std::mutex regFilesMutex;
std::condition_variable regFilesCv;
RegionsLayer layers[3] {};
util::BufferPool<ubyte> bufferPool {
std::max(CHUNK_DATA_LEN, LIGHTMAP_DATA_LEN) * 2
};
WorldRegion* getRegion(int x, int z, int layer);
WorldRegion* getOrCreateRegion(int x, int z, int layer);
/// @brief Compress buffer with extrle
/// @param src source buffer
/// @param srclen length of the source buffer
/// @param len (out argument) length of result buffer
/// @return compressed bytes array
std::unique_ptr<ubyte[]> compress(const ubyte* src, size_t srclen, size_t& len);
/// @brief Decompress buffer with extrle
/// @param src compressed buffer
/// @param srclen length of compressed buffer
/// @param dstlen max expected length of source buffer
/// @return decompressed bytes array
std::unique_ptr<ubyte[]> decompress(const ubyte* src, size_t srclen, size_t dstlen);
std::unique_ptr<ubyte[]> readChunkData(int x, int y, uint32_t& length, regfile* file);
void fetchChunks(WorldRegion* region, int x, int y, regfile* file);
ubyte* getData(int x, int z, int layer, uint32_t& size);
std::shared_ptr<regfile> getRegFile(glm::ivec3 coord, bool create=true);
void closeRegFile(glm::ivec3 coord);
std::shared_ptr<regfile> useRegFile(glm::ivec3 coord);
std::shared_ptr<regfile> createRegFile(glm::ivec3 coord);
fs::path getRegionFilename(int x, int y) const;
void writeRegions(int layer);
/// @brief Write or rewrite region file
/// @param x region X
/// @param z region Z
/// @param layer regions layer
void writeRegion(int x, int y, int layer, WorldRegion* entry);
public:
bool generatorTestMode = false;
bool doWriteLights = true;
WorldRegions(fs::path directory);
WorldRegions(const WorldRegions&) = delete;
~WorldRegions();
/// @brief Put all chunk data to regions
void put(Chunk* chunk);
/// @brief Store data in specified region
/// @param x chunk.x
/// @param z chunk.z
/// @param layer regions layer
/// @param data target data
/// @param size data size
/// @param rle compress with ext-RLE
void put(int x, int z, int layer, std::unique_ptr<ubyte[]> data, size_t size, bool rle);
std::unique_ptr<ubyte[]> getChunk(int x, int z);
std::unique_ptr<light_t[]> getLights(int x, int z);
chunk_inventories_map fetchInventories(int x, int z);
void processRegionVoxels(int x, int z, regionproc func);
fs::path getRegionsFolder(int layer) const;
void write();
/// @brief Extract X and Z from 'X_Z.bin' region file name.
/// @param name source region file name
/// @param x parsed X destination
/// @param z parsed Z destination
/// @return false if std::invalid_argument or std::out_of_range occurred
static bool parseRegionFilename(const std::string& name, int& x, int& y);
};
#endif // FILES_WORLD_REGIONS_HPP_
+60 -6
View File
@@ -1,13 +1,17 @@
#include "engine_paths.h"
#include "engine_paths.hpp"
#include <stack>
#include <sstream>
#include <filesystem>
#include <algorithm>
#include "../typedefs.h"
#include "WorldFiles.h"
#include "../util/stringutil.hpp"
#include "../typedefs.hpp"
#include "WorldFiles.hpp"
const fs::path SCREENSHOTS_FOLDER {"screenshots"};
const fs::path CONTROLS_FILE {"controls.json"};
const fs::path SETTINGS_FILE {"settings.toml"};
fs::path EnginePaths::getUserfiles() const {
return userfiles;
@@ -48,6 +52,18 @@ fs::path EnginePaths::getWorldFolder() {
return worldFolder;
}
fs::path EnginePaths::getWorldFolder(const std::string& name) {
return getWorldsFolder()/fs::path(name);
}
fs::path EnginePaths::getControlsFile() {
return userfiles/fs::path(CONTROLS_FILE);
}
fs::path EnginePaths::getSettingsFile() {
return userfiles/fs::path(SETTINGS_FILE);
}
std::vector<fs::path> EnginePaths::scanForWorlds() {
std::vector<fs::path> folders;
@@ -148,14 +164,14 @@ fs::path EnginePaths::resolve(std::string path) {
throw files_access_error("unknown entry point '"+prefix+"'");
}
ResPaths::ResPaths(fs::path mainRoot, std::vector<fs::path> roots)
ResPaths::ResPaths(fs::path mainRoot, std::vector<PathsRoot> roots)
: mainRoot(mainRoot), roots(roots) {
}
fs::path ResPaths::find(const std::string& filename) const {
for (int i = roots.size()-1; i >= 0; i--) {
auto& root = roots[i];
fs::path file = root / fs::u8path(filename);
fs::path file = root.path / fs::u8path(filename);
if (fs::exists(file)) {
return file;
}
@@ -163,11 +179,49 @@ fs::path ResPaths::find(const std::string& filename) const {
return mainRoot / fs::u8path(filename);
}
std::string ResPaths::findRaw(const std::string& filename) const {
for (int i = roots.size()-1; i >= 0; i--) {
auto& root = roots[i];
if (fs::exists(root.path / fs::path(filename))) {
return root.name + ":" + filename;
}
}
auto resDir = mainRoot;
if (fs::exists(resDir / fs::path(filename))) {
return "core:"+filename;
}
throw std::runtime_error("could not to find file "+util::quote(filename));
}
std::vector<std::string> ResPaths::listdirRaw(const std::string& folderName) const {
std::vector<std::string> entries;
for (int i = roots.size()-1; i >= 0; i--) {
auto& root = roots[i];
fs::path folder = root.path / fs::u8path(folderName);
if (!fs::is_directory(folder))
continue;
for (const auto& entry : fs::directory_iterator(folder)) {
auto name = entry.path().filename().u8string();
entries.push_back(root.name+":"+folderName+"/"+name);
}
}
{
fs::path folder = mainRoot / fs::u8path(folderName);
if (!fs::is_directory(folder))
return entries;
for (const auto& entry : fs::directory_iterator(folder)) {
auto name = entry.path().filename().u8string();
entries.push_back("core:"+folderName+"/"+name);
}
}
return entries;
}
std::vector<fs::path> ResPaths::listdir(const std::string& folderName) const {
std::vector<fs::path> entries;
for (int i = roots.size()-1; i >= 0; i--) {
auto& root = roots[i];
fs::path folder = root / fs::u8path(folderName);
fs::path folder = root.path / fs::u8path(folderName);
if (!fs::is_directory(folder))
continue;
for (const auto& entry : fs::directory_iterator(folder)) {
@@ -1,12 +1,12 @@
#ifndef FILES_ENGINE_PATHS_H_
#define FILES_ENGINE_PATHS_H_
#ifndef FILES_ENGINE_PATHS_HPP_
#define FILES_ENGINE_PATHS_HPP_
#include <string>
#include <vector>
#include <stdexcept>
#include <filesystem>
#include "../content/ContentPack.h"
#include "../content/ContentPack.hpp"
namespace fs = std::filesystem;
@@ -27,6 +27,9 @@ public:
fs::path getScreenshotFile(std::string ext);
fs::path getWorldsFolder();
fs::path getWorldFolder();
fs::path getWorldFolder(const std::string& name);
fs::path getControlsFile();
fs::path getSettingsFile();
bool isWorldNameUsed(std::string name);
void setUserfiles(fs::path folder);
@@ -39,17 +42,26 @@ public:
fs::path resolve(std::string path);
};
struct PathsRoot {
std::string name;
fs::path path;
};
class ResPaths {
fs::path mainRoot;
std::vector<fs::path> roots;
std::vector<PathsRoot> roots;
public:
ResPaths(fs::path mainRoot,
std::vector<fs::path> roots);
ResPaths(
fs::path mainRoot,
std::vector<PathsRoot> roots
);
fs::path find(const std::string& filename) const;
std::string findRaw(const std::string& filename) const;
std::vector<fs::path> listdir(const std::string& folder) const;
std::vector<std::string> listdirRaw(const std::string& folder) const;
const fs::path& getMainRoot() const;
};
#endif // FILES_ENGINE_PATHS_H_
#endif // FILES_ENGINE_PATHS_HPP_
+70 -71
View File
@@ -1,14 +1,15 @@
#include "files.h"
#include "files.hpp"
#include "../coders/json.hpp"
#include "../coders/gzip.hpp"
#include "../util/stringutil.hpp"
#include "../data/dynamic.hpp"
#include <fstream>
#include <iostream>
#include <memory>
#include <stdint.h>
#include <stdexcept>
#include "../coders/json.h"
#include "../coders/gzip.h"
#include "../util/stringutil.h"
#include "../data/dynamic.h"
namespace fs = std::filesystem;
@@ -34,64 +35,64 @@ void files::rafile::read(char* buffer, std::streamsize size) {
}
bool files::write_bytes(fs::path filename, const ubyte* data, size_t size) {
std::ofstream output(filename, std::ios::binary);
if (!output.is_open())
return false;
output.write((const char*)data, size);
output.close();
return true;
std::ofstream output(filename, std::ios::binary);
if (!output.is_open())
return false;
output.write((const char*)data, size);
output.close();
return true;
}
uint files::append_bytes(fs::path filename, const ubyte* data, size_t size) {
std::ofstream output(filename, std::ios::binary | std::ios::app);
if (!output.is_open())
return 0;
uint position = output.tellp();
output.write((const char*)data, size);
output.close();
return position;
std::ofstream output(filename, std::ios::binary | std::ios::app);
if (!output.is_open())
return 0;
uint position = output.tellp();
output.write((const char*)data, size);
output.close();
return position;
}
bool files::read(fs::path filename, char* data, size_t size) {
std::ifstream output(filename, std::ios::binary);
if (!output.is_open())
return false;
output.read(data, size);
output.close();
return true;
std::ifstream output(filename, std::ios::binary);
if (!output.is_open())
return false;
output.read(data, size);
output.close();
return true;
}
ubyte* files::read_bytes(fs::path filename, size_t& length) {
std::ifstream input(filename, std::ios::binary);
if (!input.is_open())
return nullptr;
input.seekg(0, std::ios_base::end);
length = input.tellg();
input.seekg(0, std::ios_base::beg);
std::unique_ptr<ubyte[]> files::read_bytes(fs::path filename, size_t& length) {
std::ifstream input(filename, std::ios::binary);
if (!input.is_open())
return nullptr;
input.seekg(0, std::ios_base::end);
length = input.tellg();
input.seekg(0, std::ios_base::beg);
std::unique_ptr<char> data(new char[length]);
input.read(data.get(), length);
input.close();
return (ubyte*)data.release();
auto data = std::make_unique<ubyte[]>(length);
input.read((char*)data.get(), length);
input.close();
return data;
}
std::string files::read_string(fs::path filename) {
size_t size;
std::unique_ptr<ubyte[]> bytes (read_bytes(filename, size));
if (bytes == nullptr) {
throw std::runtime_error("could not to load file '"+
filename.string()+"'");
}
return std::string((const char*)bytes.get(), size);
size_t size;
std::unique_ptr<ubyte[]> bytes (read_bytes(filename, size));
if (bytes == nullptr) {
throw std::runtime_error("could not to load file '"+
filename.string()+"'");
}
return std::string((const char*)bytes.get(), size);
}
bool files::write_string(fs::path filename, const std::string content) {
std::ofstream file(filename);
if (!file) {
return false;
}
file << content;
return true;
std::ofstream file(filename);
if (!file) {
return false;
}
file << content;
return true;
}
bool files::write_json(fs::path filename, const dynamic::Map* obj, bool nice) {
@@ -103,39 +104,37 @@ bool files::write_binary_json(fs::path filename, const dynamic::Map* obj, bool c
return files::write_bytes(filename, bytes.data(), bytes.size());
}
std::unique_ptr<dynamic::Map> files::read_json(fs::path filename) {
std::string text = files::read_string(filename);
try {
auto obj = json::parse(filename.string(), text);
std::shared_ptr<dynamic::Map> files::read_json(fs::path filename) {
std::string text = files::read_string(filename);
try {
auto obj = json::parse(filename.string(), text);
return obj;
} catch (const parsing_error& error) {
} catch (const parsing_error& error) {
std::cerr << error.errorLog() << std::endl;
throw std::runtime_error("could not to parse "+filename.string());
}
}
std::unique_ptr<dynamic::Map> files::read_binary_json(fs::path file) {
std::shared_ptr<dynamic::Map> files::read_binary_json(fs::path file) {
size_t size;
std::unique_ptr<ubyte[]> bytes (files::read_bytes(file, size));
return std::unique_ptr<dynamic::Map>(
json::from_binary(bytes.get(), size)
);
return json::from_binary(bytes.get(), size);
}
std::vector<std::string> files::read_list(fs::path filename) {
std::ifstream file(filename);
if (!file) {
throw std::runtime_error("could not to open file "+filename.u8string());
}
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line)) {
std::ifstream file(filename);
if (!file) {
throw std::runtime_error("could not to open file "+filename.u8string());
}
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line)) {
util::trim(line);
if (line.length() == 0)
continue;
if (line[0] == '#')
continue;
lines.push_back(line);
}
return lines;
if (line.length() == 0)
continue;
if (line[0] == '#')
continue;
lines.push_back(line);
}
return lines;
}
+15 -15
View File
@@ -1,12 +1,12 @@
#ifndef FILES_FILES_H_
#define FILES_FILES_H_
#ifndef FILES_FILES_HPP_
#define FILES_FILES_HPP_
#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include <filesystem>
#include "../typedefs.h"
#include "../typedefs.hpp"
namespace fs = std::filesystem;
@@ -31,20 +31,20 @@ namespace files {
/// @param file target file
/// @param data data bytes array
/// @param size size of data bytes array
extern bool write_bytes(fs::path file, const ubyte* data, size_t size);
bool write_bytes(fs::path file, const ubyte* data, size_t size);
/// @brief Append bytes array to the file without any extra data
/// @param file target file
/// @param data data bytes array
/// @param size size of data bytes array
extern uint append_bytes(fs::path file, const ubyte* data, size_t size);
uint append_bytes(fs::path file, const ubyte* data, size_t size);
/// @brief Write string to the file
extern bool write_string(fs::path filename, const std::string content);
bool write_string(fs::path filename, const std::string content);
/// @brief Write dynamic data to the JSON file
/// @param nice if true, human readable format will be used, otherwise minimal
extern bool write_json(
bool write_json(
fs::path filename,
const dynamic::Map* obj,
bool nice=true);
@@ -52,21 +52,21 @@ namespace files {
/// @brief Write dynamic data to the binary JSON file
/// (see src/coders/binary_json_spec.md)
/// @param compressed use gzip compression
extern bool write_binary_json(
bool write_binary_json(
fs::path filename,
const dynamic::Map* obj,
bool compressed=false
);
extern bool read(fs::path, char* data, size_t size);
extern ubyte* read_bytes(fs::path, size_t& length);
extern std::string read_string(fs::path filename);
bool read(fs::path, char* data, size_t size);
std::unique_ptr<ubyte[]> read_bytes(fs::path, size_t& length);
std::string read_string(fs::path filename);
/// @brief Read JSON or BJSON file
/// @param file *.json or *.bjson file
extern std::unique_ptr<dynamic::Map> read_json(fs::path file);
extern std::unique_ptr<dynamic::Map> read_binary_json(fs::path file);
extern std::vector<std::string> read_list(fs::path file);
std::shared_ptr<dynamic::Map> read_json(fs::path file);
std::shared_ptr<dynamic::Map> read_binary_json(fs::path file);
std::vector<std::string> read_list(fs::path file);
}
#endif /* FILES_FILES_H_ */
#endif /* FILES_FILES_HPP_ */
-90
View File
@@ -1,90 +0,0 @@
#include "rle.h"
size_t rle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
ubyte len = src[i++];
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t rle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
ubyte counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == 255) {
dst[offset++] = counter;
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
dst[offset++] = counter;
dst[offset++] = c;
return offset;
}
size_t extrle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
uint len = src[i++];
if (len & 0x80) {
len &= 0x7F;
len |= ((uint)src[i++]) << 7;
}
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t extrle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
uint counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == max_sequence) {
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
return offset;
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef FILES_RLE_H_
#define FILES_RLE_H_
#include "../typedefs.h"
namespace rle {
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
namespace extrle {
constexpr uint max_sequence = 0x7FFF;
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
#endif // FILES_RLE_H_
+150 -85
View File
@@ -1,99 +1,164 @@
#include "settings_io.h"
#include "settings_io.hpp"
#include "../window/Events.hpp"
#include "../window/input.hpp"
#include "../coders/toml.hpp"
#include "../coders/json.hpp"
#include "../debug/Logger.hpp"
#include "../settings.hpp"
#include <memory>
#include <iostream>
#include "../window/Events.h"
#include "../window/input.h"
static debug::Logger logger("settings_io");
#include "../coders/toml.h"
#include "../coders/json.h"
struct SectionsBuilder {
std::unordered_map<std::string, Setting*>& map;
std::vector<Section>& sections;
#include "../data/dynamic.h"
toml::Wrapper* create_wrapper(EngineSettings& settings) {
std::unique_ptr<toml::Wrapper> wrapper (new toml::Wrapper());
toml::Section& audio = wrapper->add("audio");
audio.add("enabled", &settings.audio.enabled);
audio.add("volume-master", &settings.audio.volumeMaster);
audio.add("volume-regular", &settings.audio.volumeRegular);
audio.add("volume-ui", &settings.audio.volumeUI);
audio.add("volume-ambient", &settings.audio.volumeAmbient);
audio.add("volume-music", &settings.audio.volumeMusic);
toml::Section& display = wrapper->add("display");
display.add("fullscreen", &settings.display.fullscreen);
display.add("width", &settings.display.width);
display.add("height", &settings.display.height);
display.add("samples", &settings.display.samples);
display.add("swap-interval", &settings.display.swapInterval);
toml::Section& chunks = wrapper->add("chunks");
chunks.add("load-distance", &settings.chunks.loadDistance);
chunks.add("load-speed", &settings.chunks.loadSpeed);
chunks.add("padding", &settings.chunks.padding);
toml::Section& camera = wrapper->add("camera");
camera.add("fov-effects", &settings.camera.fovEvents);
camera.add("fov", &settings.camera.fov);
camera.add("shaking", &settings.camera.shaking);
camera.add("sensitivity", &settings.camera.sensitivity);
toml::Section& graphics = wrapper->add("graphics");
graphics.add("gamma", &settings.graphics.gamma);
graphics.add("fog-curve", &settings.graphics.fogCurve);
graphics.add("backlight", &settings.graphics.backlight);
graphics.add("frustum-culling", &settings.graphics.frustumCulling);
graphics.add("skybox-resolution", &settings.graphics.skyboxResolution);
toml::Section& debug = wrapper->add("debug");
debug.add("generator-test-mode", &settings.debug.generatorTestMode);
debug.add("show-chunk-borders", &settings.debug.showChunkBorders);
debug.add("do-write-lights", &settings.debug.doWriteLights);
toml::Section& ui = wrapper->add("ui");
ui.add("language", &settings.ui.language);
return wrapper.release();
}
std::string write_controls() {
dynamic::Map obj;
for (auto& entry : Events::bindings) {
const auto& binding = entry.second;
auto& jentry = obj.putMap(entry.first);
switch (binding.type) {
case inputtype::keyboard: jentry.put("type", "keyboard"); break;
case inputtype::mouse: jentry.put("type", "mouse"); break;
default: throw std::runtime_error("unsupported control type");
}
jentry.put("code", binding.code);
SectionsBuilder(
std::unordered_map<std::string, Setting*>& map,
std::vector<Section>& sections
) : map(map), sections(sections) {
}
return json::stringify(&obj, true, " ");
void section(std::string name) {
sections.push_back(Section {name, {}});
}
void add(std::string name, Setting* setting, bool writeable=true) {
Section& section = sections.at(sections.size()-1);
map[section.name+"."+name] = setting;
section.keys.push_back(name);
}
};
SettingsHandler::SettingsHandler(EngineSettings& settings) {
SectionsBuilder builder(map, sections);
builder.section("audio");
builder.add("enabled", &settings.audio.enabled, false);
builder.add("volume-master", &settings.audio.volumeMaster);
builder.add("volume-regular", &settings.audio.volumeRegular);
builder.add("volume-ui", &settings.audio.volumeUI);
builder.add("volume-ambient", &settings.audio.volumeAmbient);
builder.add("volume-music", &settings.audio.volumeMusic);
builder.section("display");
builder.add("width", &settings.display.width);
builder.add("height", &settings.display.height);
builder.add("samples", &settings.display.samples);
builder.add("vsync", &settings.display.vsync);
builder.add("fullscreen", &settings.display.fullscreen);
builder.section("camera");
builder.add("sensitivity", &settings.camera.sensitivity);
builder.add("fov", &settings.camera.fov);
builder.add("fov-effects", &settings.camera.fovEffects);
builder.add("shaking", &settings.camera.shaking);
builder.section("chunks");
builder.add("load-distance", &settings.chunks.loadDistance);
builder.add("load-speed", &settings.chunks.loadSpeed);
builder.add("padding", &settings.chunks.padding);
builder.section("graphics");
builder.add("fog-curve", &settings.graphics.fogCurve);
builder.add("backlight", &settings.graphics.backlight);
builder.add("gamma", &settings.graphics.gamma);
builder.add("frustum-culling", &settings.graphics.frustumCulling);
builder.add("skybox-resolution", &settings.graphics.skyboxResolution);
builder.section("ui");
builder.add("language", &settings.ui.language);
builder.add("world-preview-size", &settings.ui.worldPreviewSize);
builder.section("debug");
builder.add("generator-test-mode", &settings.debug.generatorTestMode);
builder.add("do-write-lights", &settings.debug.doWriteLights);
}
void load_controls(std::string filename, std::string source) {
auto obj = json::parse(filename, source);
for (auto& entry : Events::bindings) {
auto& binding = entry.second;
dynamic::Value SettingsHandler::getValue(const std::string& name) const {
auto found = map.find(name);
if (found == map.end()) {
throw std::runtime_error("setting '"+name+"' does not exist");
}
auto setting = found->second;
if (auto number = dynamic_cast<NumberSetting*>(setting)) {
return static_cast<number_t>(number->get());
} else if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
return static_cast<integer_t>(integer->get());
} else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
return flag->get();
} else if (auto string = dynamic_cast<StringSetting*>(setting)) {
return string->get();
} else {
throw std::runtime_error("type is not implemented for '"+name+"'");
}
}
auto jentry = obj->map(entry.first);
if (jentry == nullptr)
continue;
inputtype type;
std::string typestr;
jentry->str("type", typestr);
std::string SettingsHandler::toString(const std::string& name) const {
auto found = map.find(name);
if (found == map.end()) {
throw std::runtime_error("setting '"+name+"' does not exist");
}
auto setting = found->second;
return setting->toString();
}
if (typestr == "keyboard") {
type = inputtype::keyboard;
} else if (typestr == "mouse") {
type = inputtype::mouse;
Setting* SettingsHandler::getSetting(const std::string& name) const {
auto found = map.find(name);
if (found == map.end()) {
throw std::runtime_error("setting '"+name+"' does not exist");
}
return found->second;
}
bool SettingsHandler::has(const std::string& name) const {
return map.find(name) != map.end();
}
template<class T>
static void set_numeric_value(T* setting, const dynamic::Value& value) {
if (auto num = std::get_if<integer_t>(&value)) {
setting->set(*num);
} else if (auto num = std::get_if<number_t>(&value)) {
setting->set(*num);
} else if (auto flag = std::get_if<bool>(&value)) {
setting->set(*flag);
} else {
throw std::runtime_error("type error, numeric value expected");
}
}
void SettingsHandler::setValue(const std::string& name, const dynamic::Value& value) {
auto found = map.find(name);
if (found == map.end()) {
throw std::runtime_error("setting '"+name+"' does not exist");
}
auto setting = found->second;
if (auto number = dynamic_cast<NumberSetting*>(setting)) {
set_numeric_value(number, value);
} else if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
set_numeric_value(integer, value);
} else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
set_numeric_value(flag, value);
} else if (auto string = dynamic_cast<StringSetting*>(setting)) {
if (auto num = std::get_if<integer_t>(&value)) {
string->set(std::to_string(*num));
} else if (auto num = std::get_if<number_t>(&value)) {
string->set(std::to_string(*num));
} else if (auto flag = std::get_if<bool>(&value)) {
string->set(*flag ? "true" : "false");
} else if (auto str = std::get_if<std::string>(&value)) {
string->set(*str);
} else {
std::cerr << "unknown input type '" << typestr << "'" << std::endl;
continue;
throw std::runtime_error("not implemented for type");
}
binding.type = type;
jentry->num("code", binding.code);
} else {
throw std::runtime_error("type is not implement - setting '"+name+"'");
}
}
std::vector<Section>& SettingsHandler::getSections() {
return sections;
}
-15
View File
@@ -1,15 +0,0 @@
#ifndef FILES_SETTINGS_IO_H_
#define FILES_SETTINGS_IO_H_
#include <string>
#include "../settings.h"
namespace toml {
class Wrapper;
}
extern std::string write_controls();
extern toml::Wrapper* create_wrapper(EngineSettings& settings);
extern void load_controls(std::string filename, std::string source);
#endif // FILES_SETTINGS_IO_H_
+34
View File
@@ -0,0 +1,34 @@
#ifndef FILES_SETTINGS_IO_HPP_
#define FILES_SETTINGS_IO_HPP_
#include "../data/dynamic.hpp"
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
class Setting;
struct EngineSettings;
struct Section {
std::string name;
std::vector<std::string> keys;
};
class SettingsHandler {
std::unordered_map<std::string, Setting*> map;
std::vector<Section> sections;
public:
SettingsHandler(EngineSettings& settings);
dynamic::Value getValue(const std::string& name) const;
void setValue(const std::string& name, const dynamic::Value& value);
std::string toString(const std::string& name) const;
Setting* getSetting(const std::string& name) const;
bool has(const std::string& name) const;
std::vector<Section>& getSections();
};
#endif // FILES_SETTINGS_IO_HPP_
+11 -17
View File
@@ -1,15 +1,17 @@
#include "ContentGfxCache.h"
#include "ContentGfxCache.hpp"
#include "UiDocument.hpp"
#include "../assets/Assets.hpp"
#include "../content/Content.hpp"
#include "../content/ContentPack.hpp"
#include "../core_defs.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../maths/UVRegion.hpp"
#include "../voxels/Block.hpp"
#include <string>
#include "../assets/Assets.h"
#include "../content/Content.h"
#include "../content/ContentPack.h"
#include "../graphics/Atlas.h"
#include "../voxels/Block.h"
#include "../core_defs.h"
#include "UiDocument.h"
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : content(content) {
auto indices = content->getIndices();
sideregions = std::make_unique<UVRegion[]>(indices->countBlockDefs() * 6);
@@ -39,14 +41,6 @@ ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : conte
ContentGfxCache::~ContentGfxCache() {
}
std::shared_ptr<UiDocument> ContentGfxCache::getLayout(const std::string& id) {
auto found = layouts.find(id);
if (found == layouts.end()) {
return nullptr;
}
return found->second;
}
const Content* ContentGfxCache::getContent() const {
return content;
}
-35
View File
@@ -1,35 +0,0 @@
#ifndef FRONTEND_BLOCKS_GFX_CACHE_H_
#define FRONTEND_BLOCKS_GFX_CACHE_H_
#include <memory>
#include <string>
#include <unordered_map>
#include "../graphics/UVRegion.h"
#include "../typedefs.h"
class Content;
class Assets;
class UiDocument;
using uidocuments_map = std::unordered_map<std::string, std::shared_ptr<UiDocument>>;
class ContentGfxCache {
const Content* content;
// array of block sides uv regions (6 per block)
std::unique_ptr<UVRegion[]> sideregions;
// all loaded layouts
uidocuments_map layouts;
public:
ContentGfxCache(const Content* content, Assets* assets);
~ContentGfxCache();
inline const UVRegion& getRegion(blockid_t id, int side) const {
return sideregions[id * 6 + side];
}
std::shared_ptr<UiDocument> getLayout(const std::string& id);
const Content* getContent() const;
};
#endif // FRONTEND_BLOCKS_GFX_CACHE_H_
+27
View File
@@ -0,0 +1,27 @@
#ifndef FRONTEND_BLOCKS_GFX_CACHE_HPP_
#define FRONTEND_BLOCKS_GFX_CACHE_HPP_
#include "../typedefs.hpp"
#include <memory>
class Content;
class Assets;
struct UVRegion;
class ContentGfxCache {
const Content* content;
// array of block sides uv regions (6 per block)
std::unique_ptr<UVRegion[]> sideregions;
public:
ContentGfxCache(const Content* content, Assets* assets);
~ContentGfxCache();
inline const UVRegion& getRegion(blockid_t id, int side) const {
return sideregions[id * 6 + side];
}
const Content* getContent() const;
};
#endif // FRONTEND_BLOCKS_GFX_CACHE_HPP_
-160
View File
@@ -1,160 +0,0 @@
#ifndef FRONTEND_INVENTORY_VIEW_H_
#define FRONTEND_INVENTORY_VIEW_H_
#include <vector>
#include <functional>
#include <glm/glm.hpp>
#include "../frontend/gui/UINode.h"
#include "../frontend/gui/containers.h"
#include "../frontend/gui/controls.h"
#include "../items/ItemStack.h"
#include "../typedefs.h"
class Assets;
class GfxContext;
class Content;
class ContentIndices;
class LevelFrontend;
class Inventory;
namespace gui {
class UiXmlReader;
}
namespace scripting {
class Environment;
}
using slotcallback = std::function<void(uint, ItemStack&)>;
class InventoryInteraction {
ItemStack grabbedItem;
public:
InventoryInteraction() = default;
ItemStack& getGrabbedItem() {
return grabbedItem;
}
};
struct SlotLayout {
int index;
glm::vec2 position;
bool background;
bool itemSource;
slotcallback updateFunc;
slotcallback shareFunc;
slotcallback rightClick;
int padding = 0;
SlotLayout(
int index,
glm::vec2 position,
bool background,
bool itemSource,
slotcallback updateFunc,
slotcallback shareFunc,
slotcallback rightClick
);
};
class SlotView : public gui::UINode {
LevelFrontend* frontend = nullptr;
InventoryInteraction* interaction = nullptr;
const Content* content;
SlotLayout layout;
bool highlighted = false;
int64_t inventoryid = 0;
ItemStack* bound = nullptr;
public:
SlotView(SlotLayout layout);
virtual void draw(const GfxContext* pctx, Assets* assets) override;
void setHighlighted(bool flag);
bool isHighlighted() const;
virtual void clicked(gui::GUI*, mousecode) override;
virtual void onFocus(gui::GUI*) override;
void bind(
int64_t inventoryid,
ItemStack& stack,
LevelFrontend* frontend,
InventoryInteraction* interaction
);
const SlotLayout& getLayout() const;
};
class InventoryView : public gui::Container {
const Content* content;
const ContentIndices* indices;
std::shared_ptr<Inventory> inventory;
LevelFrontend* frontend = nullptr;
InventoryInteraction* interaction = nullptr;
std::vector<SlotView*> slots;
glm::vec2 origin {};
public:
InventoryView();
virtual ~InventoryView();
void setInventory(std::shared_ptr<Inventory> inventory);
virtual void setPos(glm::vec2 pos) override;
void setOrigin(glm::vec2 origin);
glm::vec2 getOrigin() const;
void setSelected(int index);
void bind(
std::shared_ptr<Inventory> inventory,
LevelFrontend* frontend,
InventoryInteraction* interaction
);
void unbind();
std::shared_ptr<SlotView> addSlot(SlotLayout layout);
std::shared_ptr<Inventory> getInventory() const;
size_t getSlotsCount() const;
static void createReaders(gui::UiXmlReader& reader);
static const int SLOT_INTERVAL = 4;
static const int SLOT_SIZE = ITEM_ICON_SIZE;
};
class InventoryBuilder {
std::shared_ptr<InventoryView> view;
public:
InventoryBuilder();
/// @brief Add slots grid to inventory view
/// @param cols grid columns
/// @param count total number of grid slots
/// @param pos position of the first slot of the grid
/// @param padding additional space around the grid
/// @param addpanel automatically create panel behind the grid
/// with size including padding
/// @param slotLayout slot settings (index and position are ignored)
void addGrid(
int cols, int count,
glm::vec2 pos,
int padding,
bool addpanel,
SlotLayout slotLayout
);
void add(SlotLayout slotLayout);
std::shared_ptr<InventoryView> build();
};
#endif // FRONTEND_INVENTORY_VIEW_H_
+16 -18
View File
@@ -1,25 +1,27 @@
#include "LevelFrontend.h"
#include "LevelFrontend.hpp"
#include "BlocksPreview.h"
#include "ContentGfxCache.h"
#include "ContentGfxCache.hpp"
#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"
#include "../assets/Assets.hpp"
#include "../audio/audio.hpp"
#include "../content/Content.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../graphics/render/BlocksPreview.hpp"
#include "../logic/LevelController.hpp"
#include "../logic/PlayerController.hpp"
#include "../voxels/Block.hpp"
#include "../world/Level.hpp"
LevelFrontend::LevelFrontend(LevelController* controller, Assets* assets)
: level(controller->getLevel()),
controller(controller),
assets(assets),
contentCache(std::make_unique<ContentGfxCache>(level->content, assets)),
blocksAtlas(BlocksPreview::build(contentCache.get(), assets, level->content))
contentCache(std::make_unique<ContentGfxCache>(level->content, assets))
{
assets->store(
BlocksPreview::build(contentCache.get(), assets, level->content).release(),
"block-previews"
);
controller->getPlayerController()->listenBlockInteraction(
[=](Player*, glm::ivec3 pos, const Block* def, BlockInteraction type) {
auto material = level->content->findBlockMaterial(def->material);
@@ -81,10 +83,6 @@ ContentGfxCache* LevelFrontend::getContentGfxCache() const {
return contentCache.get();
}
Atlas* LevelFrontend::getBlocksAtlas() const {
return blocksAtlas.get();
}
LevelController* LevelFrontend::getController() const {
return controller;
}
@@ -1,12 +1,10 @@
#ifndef FRONTEND_LEVEL_FRONTEND_H_
#define FRONTEND_LEVEL_FRONTEND_H_
#ifndef FRONTEND_LEVEL_FRONTEND_HPP_
#define FRONTEND_LEVEL_FRONTEND_HPP_
#include <memory>
class Atlas;
class Level;
class Assets;
class BlocksPreview;
class ContentGfxCache;
class LevelController;
@@ -15,7 +13,6 @@ class LevelFrontend {
LevelController* controller;
Assets* assets;
std::unique_ptr<ContentGfxCache> contentCache;
std::unique_ptr<Atlas> blocksAtlas;
public:
LevelFrontend(LevelController* controller, Assets* assets);
~LevelFrontend();
@@ -23,9 +20,7 @@ public:
Level* getLevel() const;
Assets* getAssets() const;
ContentGfxCache* getContentGfxCache() const;
Atlas* getBlocksAtlas() const;
LevelController* getController() const;
};
#endif // FRONTEND_LEVEL_FRONTEND_H_
#endif // FRONTEND_LEVEL_FRONTEND_HPP_
+31 -32
View File
@@ -1,27 +1,32 @@
#include "UiDocument.h"
#include "UiDocument.hpp"
#include <iostream>
#include "gui/UINode.h"
#include "gui/containers.h"
#include "InventoryView.h"
#include "../logic/scripting/scripting.h"
#include "../files/files.h"
#include "../frontend/gui/gui_xml.h"
#include "../files/files.hpp"
#include "../graphics/ui/elements/UINode.hpp"
#include "../graphics/ui/elements/InventoryView.hpp"
#include "../graphics/ui/gui_xml.hpp"
#include "../logic/scripting/scripting.hpp"
UiDocument::UiDocument(
std::string id,
uidocscript script,
std::shared_ptr<gui::UINode> root,
std::unique_ptr<scripting::Environment> env
) : id(id), script(script), root(root), env(std::move(env)) {
collect(map, root);
scriptenv env
) : id(id), script(script), root(root), env(env) {
gui::UINode::getIndices(root, map);
}
void UiDocument::rebuildIndices() {
gui::UINode::getIndices(root, map);
}
const uinodes_map& UiDocument::getMap() const {
return map;
}
uinodes_map& UiDocument::getMapWriteable() {
return map;
}
const std::string& UiDocument::getId() const {
return id;
}
@@ -42,30 +47,19 @@ const uidocscript& UiDocument::getScript() const {
return script;
}
int UiDocument::getEnvironment() const {
return env->getId();
scriptenv UiDocument::getEnvironment() const {
return env;
}
void UiDocument::collect(uinodes_map& map, std::shared_ptr<gui::UINode> node) {
const std::string& id = node->getId();
if (!id.empty()) {
map[id] = node;
}
auto container = dynamic_cast<gui::Container*>(node.get());
if (container) {
for (auto subnode : container->getNodes()) {
collect(map, subnode);
}
}
}
std::unique_ptr<UiDocument> UiDocument::read(AssetsLoader& loader, int penv, std::string namesp, fs::path file) {
std::unique_ptr<UiDocument> UiDocument::read(scriptenv penv, std::string name, fs::path file) {
const std::string text = files::read_string(file);
auto xmldoc = xml::parse(file.u8string(), text);
auto env = scripting::create_doc_environment(penv, namesp);
gui::UiXmlReader reader(*env, loader);
InventoryView::createReaders(reader);
auto env = penv == nullptr
? scripting::create_doc_environment(scripting::get_root_environment(), name)
: scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env);
auto view = reader.readXML(
file.u8string(), xmldoc->getRoot()
);
@@ -73,7 +67,12 @@ std::unique_ptr<UiDocument> UiDocument::read(AssetsLoader& loader, int penv, std
uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua");
if (fs::is_regular_file(scriptFile)) {
scripting::load_layout_script(env->getId(), namesp, scriptFile, script);
scripting::load_layout_script(env, name, scriptFile, script);
}
return std::make_unique<UiDocument>(namesp, script, view, std::move(env));
return std::make_unique<UiDocument>(name, script, view, env);
}
std::shared_ptr<gui::UINode> UiDocument::readElement(fs::path file) {
auto document = read(nullptr, file.filename().u8string(), file);
return document->getRoot();
}
@@ -1,5 +1,7 @@
#ifndef FRONTEND_UI_DOCUMENT_H_
#define FRONTEND_UI_DOCUMENT_H_
#ifndef FRONTEND_UI_DOCUMENT_HPP_
#define FRONTEND_UI_DOCUMENT_HPP_
#include "../typedefs.hpp"
#include <string>
#include <memory>
@@ -12,44 +14,40 @@ namespace gui {
class UINode;
}
namespace scripting {
class Environment;
}
struct uidocscript {
int environment;
bool onopen : 1;
bool onprogress : 1;
bool onclose : 1;
};
using uinodes_map = std::unordered_map<std::string, std::shared_ptr<gui::UINode>>;
class AssetsLoader;
class UiDocument {
std::string id;
uidocscript script;
uinodes_map map;
std::shared_ptr<gui::UINode> root;
std::unique_ptr<scripting::Environment> env;
scriptenv env;
public:
UiDocument(
std::string id,
uidocscript script,
std::shared_ptr<gui::UINode> root,
std::unique_ptr<scripting::Environment> env
scriptenv env
);
void rebuildIndices();
const std::string& getId() const;
const uinodes_map& getMap() const;
uinodes_map& getMapWriteable();
const std::shared_ptr<gui::UINode> getRoot() const;
const std::shared_ptr<gui::UINode> get(const std::string& id) const;
const uidocscript& getScript() const;
int getEnvironment() const;
/* Collect map of all uinodes having identifiers */
static void collect(uinodes_map& map, std::shared_ptr<gui::UINode> node);
scriptenv getEnvironment() const;
static std::unique_ptr<UiDocument> read(AssetsLoader& loader, int env, std::string namesp, fs::path file);
static std::unique_ptr<UiDocument> read(scriptenv parent_env, std::string name, fs::path file);
static std::shared_ptr<gui::UINode> readElement(fs::path file);
};
#endif // FRONTEND_UI_DOCUMENT_H_
#endif // FRONTEND_UI_DOCUMENT_HPP_
+27 -23
View File
@@ -1,22 +1,25 @@
#include "../audio/audio.hpp"
#include "../delegates.hpp"
#include "../engine.hpp"
#include "../graphics/core/Mesh.hpp"
#include "../graphics/ui/elements/CheckBox.hpp"
#include "../graphics/ui/elements/TextBox.hpp"
#include "../graphics/ui/elements/TrackBar.hpp"
#include "../graphics/ui/elements/InputBindBox.hpp"
#include "../graphics/render/WorldRenderer.hpp"
#include "../objects/Player.hpp"
#include "../physics/Hitbox.hpp"
#include "../util/stringutil.hpp"
#include "../voxels/Block.hpp"
#include "../voxels/Chunk.hpp"
#include "../voxels/Chunks.hpp"
#include "../world/Level.hpp"
#include "../world/World.hpp"
#include <string>
#include <memory>
#include <sstream>
#include "gui/controls.h"
#include "../audio/audio.h"
#include "../graphics/Mesh.h"
#include "../objects/Player.h"
#include "../physics/Hitbox.h"
#include "../world/Level.h"
#include "../world/World.h"
#include "../voxels/Chunks.h"
#include "../voxels/Block.h"
#include "../util/stringutil.h"
#include "../delegates.h"
#include "../engine.h"
#include "WorldRenderer.h"
using namespace gui;
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
@@ -60,7 +63,7 @@ std::shared_ptr<UINode> create_debug_panel(
}));
panel->add(create_label([=](){
auto& settings = engine->getSettings();
bool culling = settings.graphics.frustumCulling;
bool culling = settings.graphics.frustumCulling.get();
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
}));
panel->add(create_label([=]() {
@@ -79,7 +82,7 @@ std::shared_ptr<UINode> create_debug_panel(
L" "+stream.str();
}));
panel->add(create_label([=](){
return L"seed: "+std::to_wstring(level->world->getSeed());
return L"seed: "+std::to_wstring(level->getWorld()->getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
@@ -95,6 +98,7 @@ std::shared_ptr<UINode> create_debug_panel(
// Coord input
auto box = std::make_shared<TextBox>(L"");
auto boxRef = box.get();
box->setTextSupplier([=]() {
Hitbox* hitbox = player->hitbox.get();
return util::to_wstring(hitbox->position[ax], 2);
@@ -109,7 +113,7 @@ std::shared_ptr<UINode> create_debug_panel(
});
box->setOnEditStart([=](){
Hitbox* hitbox = player->hitbox.get();
box->setText(std::to_wstring(int(hitbox->position[ax])));
boxRef->setText(std::to_wstring(int(hitbox->position[ax])));
});
box->setSize(glm::vec2(230, 27));
@@ -118,7 +122,7 @@ std::shared_ptr<UINode> create_debug_panel(
}
panel->add(create_label([=](){
int hour, minute, second;
timeutil::from_value(level->world->daytime, hour, minute, second);
timeutil::from_value(level->getWorld()->daytime, hour, minute, second);
std::wstring timeString =
util::lfill(std::to_wstring(hour), 2, L'0') + L":" +
@@ -127,8 +131,8 @@ std::shared_ptr<UINode> create_debug_panel(
}));
{
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
bar->setSupplier([=]() {return level->world->daytime;});
bar->setConsumer([=](double val) {level->world->daytime = val;});
bar->setSupplier([=]() {return level->getWorld()->daytime;});
bar->setConsumer([=](double val) {level->getWorld()->daytime = val;});
panel->add(bar);
}
{
@@ -142,10 +146,10 @@ std::shared_ptr<UINode> create_debug_panel(
L"Show Chunk Borders", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return engine->getSettings().debug.showChunkBorders;
return WorldRenderer::showChunkBorders;
});
checkbox->setConsumer([=](bool checked) {
engine->getSettings().debug.showChunkBorders = checked;
WorldRenderer::showChunkBorders = checked;
});
panel->add(checkbox);
}
-472
View File
@@ -1,472 +0,0 @@
#include "BlocksRenderer.h"
#include <glm/glm.hpp>
#include "../../graphics/Mesh.h"
#include "../../graphics/UVRegion.h"
#include "../../constants.h"
#include "../../content/Content.h"
#include "../../voxels/Block.h"
#include "../../voxels/Chunk.h"
#include "../../voxels/VoxelsVolume.h"
#include "../../voxels/ChunksStorage.h"
#include "../../lighting/Lightmap.h"
#include "../../frontend/ContentGfxCache.h"
using glm::ivec3;
using glm::vec3;
using glm::vec4;
const uint BlocksRenderer::VERTEX_SIZE = 6;
const vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
BlocksRenderer::BlocksRenderer(size_t capacity,
const Content* content,
const ContentGfxCache* cache,
const EngineSettings& settings)
: content(content),
vertexOffset(0),
indexOffset(0),
indexSize(0),
capacity(capacity),
cache(cache),
settings(settings) {
vertexBuffer = new float[capacity];
indexBuffer = new int[capacity];
voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2);
blockDefsCache = content->getIndices()->getBlockDefs();
}
BlocksRenderer::~BlocksRenderer() {
delete voxelsBuffer;
delete[] vertexBuffer;
delete[] indexBuffer;
}
/* Basic vertex add method */
void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& light) {
vertexBuffer[vertexOffset++] = coord.x;
vertexBuffer[vertexOffset++] = coord.y;
vertexBuffer[vertexOffset++] = coord.z;
vertexBuffer[vertexOffset++] = u;
vertexBuffer[vertexOffset++] = v;
union {
float floating;
uint32_t integer;
} compressed;
compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24;
compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16;
compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8;
compressed.integer |= (uint32_t(light.a * 255) & 0xff);
vertexBuffer[vertexOffset++] = compressed.floating;
}
void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) {
indexBuffer[indexSize++] = indexOffset + a;
indexBuffer[indexSize++] = indexOffset + b;
indexBuffer[indexSize++] = indexOffset + c;
indexBuffer[indexSize++] = indexOffset + d;
indexBuffer[indexSize++] = indexOffset + e;
indexBuffer[indexSize++] = indexOffset + f;
indexOffset += 4;
}
/* Add face with precalculated lights */
void BlocksRenderer::face(const vec3& coord,
float w, float h, float d,
const vec3& axisX,
const vec3& axisY,
const vec3& axisZ,
const UVRegion& region,
const vec4(&lights)[4],
const vec4& tint) {
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
overflow = true;
return;
}
vec3 X = axisX * w;
vec3 Y = axisY * h;
vec3 Z = axisZ * d;
float s = 0.5f;
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint);
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint);
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint);
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint);
index(0, 1, 3, 1, 2, 3);
}
void BlocksRenderer::vertex(const vec3& coord,
float u, float v,
const vec4& tint,
const vec3& axisX,
const vec3& axisY,
const vec3& axisZ) {
vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f;
vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY);
vertex(coord, u, v, light * tint);
}
void BlocksRenderer::face(const vec3& coord,
const vec3& X,
const vec3& Y,
const vec3& Z,
const UVRegion& region,
bool lights) {
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
overflow = true;
return;
}
float s = 0.5f;
if (lights) {
float d = glm::dot(Z, SUN_VECTOR);
d = 0.8f + d * 0.2f;
vec3 axisX = glm::normalize(X);
vec3 axisY = glm::normalize(Y);
vec3 axisZ = glm::normalize(Z);
vec4 tint(d);
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ);
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ);
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ);
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ);
} else {
vec4 tint(1.0f);
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint);
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint);
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint);
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint);
}
index(0, 1, 2, 0, 2, 3);
}
void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1,
const vec3& p2, const vec3& p3, const vec3& p4,
const vec3& X,
const vec3& Y,
const vec3& Z,
const UVRegion& texreg,
bool lights) {
const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z;
const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z;
const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z;
const vec3 fp4 = (p4.x - 0.5f) * X + (p4.y - 0.5f) * Y + (p4.z - 0.5f) * Z;
vec4 tint(1.0f);
if (lights) {
vec3 dir = glm::cross(fp2 - fp1, fp3 - fp1);
vec3 normal = glm::normalize(dir);
float d = glm::dot(normal, SUN_VECTOR);
d = 0.8f + d * 0.2f;
tint *= d;
tint *= pickLight(coord);
// debug normal
// tint.x = normal.x * 0.5f + 0.5f;
// tint.y = normal.y * 0.5f + 0.5f;
// tint.z = normal.z * 0.5f + 0.5f;
}
vertex(coord + fp1, texreg.u1, texreg.v1, tint);
vertex(coord + fp2, texreg.u2, texreg.v1, tint);
vertex(coord + fp3, texreg.u2, texreg.v2, tint);
vertex(coord + fp4, texreg.u1, texreg.v2, tint);
index(0, 1, 3, 1, 2, 3);
}
void BlocksRenderer::blockXSprite(int x, int y, int z,
const vec3& size,
const UVRegion& texface1,
const UVRegion& texface2,
float spread) {
vec4 lights[]{
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) };
int rand = ((x * z + y) ^ (z * y - x)) * (z + y);
float xs = ((float)(char)rand / 512) * spread;
float zs = ((float)(char)(rand >> 8) / 512) * spread;
const float w = size.x / 1.41f;
const float tint = 0.8f;
face(vec3(x + xs, y, z + zs),
w, size.y, 0, vec3(1, 0, 1), vec3(0, 1, 0), vec3(),
texface1, lights, vec4(tint));
face(vec3(x + xs, y, z + zs),
w, size.y, 0, vec3(-1, 0, -1), vec3(0, 1, 0), vec3(),
texface1, lights, vec4(tint));
face(vec3(x + xs, y, z + zs),
w, size.y, 0, vec3(1, 0, -1), vec3(0, 1, 0), vec3(),
texface1, lights, vec4(tint));
face(vec3(x + xs, y, z + zs),
w, size.y, 0, vec3(-1, 0, 1), vec3(0, 1, 0), vec3(),
texface1, lights, vec4(tint));
}
// HINT: texture faces order: {east, west, bottom, top, south, north}
/* AABB blocks render method */
void BlocksRenderer::blockAABB(const ivec3& icoord,
const UVRegion(&texfaces)[6],
const Block* block, ubyte rotation,
bool lights) {
if (block->hitboxes.empty()) {
return;
}
AABB hitbox = block->hitboxes[0];
for (const auto& box : block->hitboxes) {
hitbox.a = glm::min(hitbox.a, box.a);
hitbox.b = glm::max(hitbox.b, box.b);
}
vec3 size = hitbox.size();
vec3 X(1, 0, 0);
vec3 Y(0, 1, 0);
vec3 Z(0, 0, 1);
vec3 coord(icoord);
if (block->rotatable) {
auto& rotations = block->rotations;
auto& orient = rotations.variants[rotation];
X = orient.axisX;
Y = orient.axisY;
Z = orient.axisZ;
orient.transform(hitbox);
}
coord = vec3(icoord) - vec3(0.5f) + hitbox.center();
face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north
face(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], lights); // south
face(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], lights); // top
face(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], lights); // bottom
face(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], lights); // west
face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east
}
void BlocksRenderer::blockCustomModel(const ivec3& icoord,
const Block* block, ubyte rotation, bool lights) {
vec3 X(1, 0, 0);
vec3 Y(0, 1, 0);
vec3 Z(0, 0, 1);
CoordSystem orient(X,Y,Z);
vec3 coord(icoord);
if (block->rotatable) {
auto& rotations = block->rotations;
orient = rotations.variants[rotation];
X = orient.axisX;
Y = orient.axisY;
Z = orient.axisZ;
}
for (size_t i = 0; i < block->modelBoxes.size(); i++) {
AABB box = block->modelBoxes[i];
vec3 size = box.size();
if (block->rotatable) {
orient.transform(box);
}
vec3 center_coord = coord - vec3(0.5f) + box.center();
face(center_coord, X * size.x, Y * size.y, Z * size.z, block->modelUVs[i * 6 + 5], lights); // north
face(center_coord, -X * size.x, Y * size.y, -Z * size.z, block->modelUVs[i * 6 + 4], lights); // south
face(center_coord, X * size.x, -Z * size.z, Y * size.y, block->modelUVs[i * 6 + 3], lights); // top
face(center_coord, -X * size.x, -Z * size.z, -Y * size.y, block->modelUVs[i * 6 + 2], lights); // bottom
face(center_coord, -Z * size.z, Y * size.y, X * size.x, block->modelUVs[i * 6 + 1], lights); // west
face(center_coord, Z * size.z, Y * size.y, -X * size.x, block->modelUVs[i * 6 + 0], lights); // east
}
for (size_t i = 0; i < block->modelExtraPoints.size()/4; i++) {
tetragonicFace(coord,
block->modelExtraPoints[i * 4 + 0],
block->modelExtraPoints[i * 4 + 1],
block->modelExtraPoints[i * 4 + 2],
block->modelExtraPoints[i * 4 + 3],
X, Y, Z,
block->modelUVs[block->modelBoxes.size()*6 + i], lights);
}
}
/* Fastest solid shaded blocks render method */
void BlocksRenderer::blockCube(int x, int y, int z,
const UVRegion(&texfaces)[6],
const Block* block,
ubyte states,
bool lights) {
ubyte group = block->drawGroup;
vec3 X(1, 0, 0);
vec3 Y(0, 1, 0);
vec3 Z(0, 0, 1);
vec3 coord(x, y, z);
if (block->rotatable) {
auto& rotations = block->rotations;
auto& orient = rotations.variants[states & BLOCK_ROT_MASK];
X = orient.axisX;
Y = orient.axisY;
Z = orient.axisZ;
}
if (isOpen(x+Z.x, y+Z.y, z+Z.z, group)) {
face(coord, X, Y, Z, texfaces[5], lights);
}
if (isOpen(x-Z.x, y-Z.y, z-Z.z, group)) {
face(coord, -X, Y, -Z, texfaces[4], lights);
}
if (isOpen(x+Y.x, y+Y.y, z+Y.z, group)) {
face(coord, X, -Z, Y, texfaces[3], lights);
}
if (isOpen(x-Y.x, y-Y.y, z-Y.z, group)) {
face(coord, X, Z, -Y, texfaces[2], lights);
}
if (isOpen(x+X.x, y+X.y, z+X.z, group)) {
face(coord, -Z, Y, X, texfaces[1], lights);
}
if (isOpen(x-X.x, y-X.y, z-X.z, group)) {
face(coord, Z, Y, -X, texfaces[0], lights);
}
}
// Does block allow to see other blocks sides (is it transparent)
bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) const {
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
y,
chunk->z * CHUNK_D + z);
if (id == BLOCK_VOID)
return false;
const Block& block = *blockDefsCache[id];
if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) {
return true;
}
return !id;
}
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
y,
chunk->z * CHUNK_D + z);
if (id == BLOCK_VOID)
return false;
const Block& block = *blockDefsCache[id];
if (block.lightPassing) {
return true;
}
return !id;
}
vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
if (isOpenForLight(x, y, z)) {
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x,
y,
chunk->z * CHUNK_D + z);
return vec4(Lightmap::extract(light, 0) / 15.0f,
Lightmap::extract(light, 1) / 15.0f,
Lightmap::extract(light, 2) / 15.0f,
Lightmap::extract(light, 3) / 15.0f);
}
else {
return vec4(0.0f);
}
}
vec4 BlocksRenderer::pickLight(const ivec3& coord) const {
return pickLight(coord.x, coord.y, coord.z);
}
vec4 BlocksRenderer::pickSoftLight(const ivec3& coord,
const ivec3& right,
const ivec3& up) const {
return (
pickLight(coord) +
pickLight(coord - right) +
pickLight(coord - right - up) +
pickLight(coord - up)) * 0.25f;
}
vec4 BlocksRenderer::pickSoftLight(float x, float y, float z,
const ivec3& right,
const ivec3& up) const {
return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up);
}
void BlocksRenderer::render(const voxel* voxels) {
int begin = chunk->bottom * (CHUNK_W * CHUNK_D);
int end = chunk->top * (CHUNK_W * CHUNK_D);
for (const auto drawGroup : *content->drawGroups) {
for (int i = begin; i < end; i++) {
const voxel& vox = voxels[i];
blockid_t id = vox.id;
const Block& def = *blockDefsCache[id];
if (id == 0 || def.drawGroup != drawGroup)
continue;
const UVRegion texfaces[6]{ cache->getRegion(id, 0),
cache->getRegion(id, 1),
cache->getRegion(id, 2),
cache->getRegion(id, 3),
cache->getRegion(id, 4),
cache->getRegion(id, 5)};
int x = i % CHUNK_W;
int y = i / (CHUNK_D * CHUNK_W);
int z = (i / CHUNK_D) % CHUNK_W;
switch (def.model) {
case BlockModel::block:
blockCube(x, y, z, texfaces, &def, vox.states, !def.rt.emissive);
break;
case BlockModel::xsprite: {
blockXSprite(x, y, z, vec3(1.0f),
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
break;
}
case BlockModel::aabb: {
blockAABB(ivec3(x,y,z), texfaces, &def, vox.rotation(), !def.rt.emissive);
break;
}
case BlockModel::custom: {
blockCustomModel(ivec3(x, y, z), &def, vox.rotation(), !def.rt.emissive);
break;
}
default:
break;
}
if (overflow)
return;
}
}
}
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
this->chunk = chunk;
voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1);
chunks->getVoxels(voxelsBuffer, settings.graphics.backlight);
overflow = false;
vertexOffset = 0;
indexOffset = indexSize = 0;
const voxel* voxels = chunk->voxels;
render(voxels);
}
Mesh* BlocksRenderer::createMesh() {
const vattr attrs[]{ {3}, {2}, {1}, {0} };
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs);
return mesh;
}
Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
build(chunk, chunks);
return createMesh();
}
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
return voxelsBuffer;
}
-102
View File
@@ -1,102 +0,0 @@
#ifndef GRAPHICS_BLOCKS_RENDERER_H
#define GRAPHICS_BLOCKS_RENDERER_H
#include <stdlib.h>
#include <vector>
#include <glm/glm.hpp>
#include "../../graphics/UVRegion.h"
#include "../../voxels/voxel.h"
#include "../../typedefs.h"
#include "../../settings.h"
class Content;
class Mesh;
class Block;
class Chunk;
class Chunks;
class VoxelsVolume;
class ChunksStorage;
class ContentGfxCache;
class BlocksRenderer {
static const glm::vec3 SUN_VECTOR;
static const uint VERTEX_SIZE;
const Content* const content;
float* vertexBuffer;
int* indexBuffer;
size_t vertexOffset;
size_t indexOffset, indexSize;
size_t capacity;
bool overflow = false;
const Chunk* chunk = nullptr;
VoxelsVolume* voxelsBuffer;
const Block* const* blockDefsCache;
const ContentGfxCache* const cache;
const EngineSettings& settings;
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
void index(int a, int b, int c, int d, int e, int f);
void vertex(const glm::vec3& coord, float u, float v,
const glm::vec4& brightness,
const glm::vec3& axisX,
const glm::vec3& axisY,
const glm::vec3& axisZ);
void face(const glm::vec3& coord, float w, float h, float d,
const glm::vec3& axisX,
const glm::vec3& axisY,
const glm::vec3& axisZ,
const UVRegion& region,
const glm::vec4(&lights)[4],
const glm::vec4& tint);
void face(const glm::vec3& coord,
const glm::vec3& axisX,
const glm::vec3& axisY,
const glm::vec3& axisZ,
const UVRegion& region,
bool lights);
void tetragonicFace(const glm::vec3& coord,
const glm::vec3& p1, const glm::vec3& p2,
const glm::vec3& p3, const glm::vec3& p4,
const glm::vec3& X,
const glm::vec3& Y,
const glm::vec3& Z,
const UVRegion& texreg,
bool lights);
void blockCube(int x, int y, int z, const UVRegion(&faces)[6], const Block* block, ubyte states, bool lights);
void blockAABB(const glm::ivec3& coord,
const UVRegion(&faces)[6],
const Block* block,
ubyte rotation,
bool lights);
void blockXSprite(int x, int y, int z, const glm::vec3& size, const UVRegion& face1, const UVRegion& face2, float spread);
void blockCustomModel(const glm::ivec3& icoord,
const Block* block, ubyte rotation,
bool lights);
bool isOpenForLight(int x, int y, int z) const;
bool isOpen(int x, int y, int z, ubyte group) const;
glm::vec4 pickLight(int x, int y, int z) const;
glm::vec4 pickLight(const glm::ivec3& coord) const;
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
void render(const voxel* voxels);
public:
BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings& settings);
virtual ~BlocksRenderer();
void build(const Chunk* chunk, const ChunksStorage* chunks);
Mesh* render(const Chunk* chunk, const ChunksStorage* chunks);
Mesh* createMesh();
VoxelsVolume* getVoxelsBuffer() const;
};
#endif // GRAPHICS_BLOCKS_RENDERER_H
-150
View File
@@ -1,150 +0,0 @@
#include "ChunksRenderer.h"
#include "../../graphics/Mesh.h"
#include "BlocksRenderer.h"
#include "../../voxels/Chunk.h"
#include "../../world/Level.h"
#include <iostream>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings)
: level(level), cache(cache), settings(settings) {
const int MAX_FULL_CUBES = 3000;
renderer = std::make_unique<BlocksRenderer>(
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
);
const uint num_threads = std::thread::hardware_concurrency();
for (uint i = 0; i < num_threads; i++) {
threads.emplace_back(&ChunksRenderer::threadLoop, this, i);
workersBlocked.emplace_back();
}
std::cout << "created " << num_threads << " chunks rendering threads" << std::endl;
}
ChunksRenderer::~ChunksRenderer() {
{
std::unique_lock<std::mutex> lock(jobsMutex);
working = false;
}
resultsMutex.lock();
while (!results.empty()) {
mesh_entry entry = results.front();
results.pop();
entry.locked = false;
entry.variable.notify_all();
}
resultsMutex.unlock();
jobsMutexCondition.notify_all();
for (auto& thread : threads) {
thread.join();
}
}
void ChunksRenderer::threadLoop(int index) {
const int MAX_FULL_CUBES = 3000;
BlocksRenderer renderer(
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
);
std::condition_variable variable;
std::mutex mutex;
bool locked = false;
while (working) {
std::shared_ptr<Chunk> chunk;
{
std::unique_lock<std::mutex> lock(jobsMutex);
jobsMutexCondition.wait(lock, [this] {
return !jobs.empty() || !working;
});
if (!working) {
break;
}
chunk = jobs.front();
jobs.pop();
}
process(chunk, renderer);
{
resultsMutex.lock();
results.push(mesh_entry {renderer, variable, index, locked, glm::ivec2(chunk->x, chunk->z)});
locked = true;
resultsMutex.unlock();
}
{
std::unique_lock<std::mutex> lock(mutex);
variable.wait(lock, [&] {
return !working || !locked;
});
}
}
}
void ChunksRenderer::process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer) {
renderer.build(chunk.get(), level->chunksStorage.get());
}
std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) {
chunk->setModified(false);
if (important) {
Mesh* mesh = renderer->render(chunk.get(), level->chunksStorage.get());
auto sptr = std::shared_ptr<Mesh>(mesh);
meshes[glm::ivec2(chunk->x, chunk->z)] = sptr;
return sptr;
}
glm::ivec2 key(chunk->x, chunk->z);
if (inwork.find(key) != inwork.end()) {
return nullptr;
}
inwork[key] = true;
jobsMutex.lock();
jobs.push(chunk);
jobsMutex.unlock();
jobsMutexCondition.notify_one();
return nullptr;
}
void ChunksRenderer::unload(Chunk* chunk) {
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found != meshes.end()) {
meshes.erase(found);
}
}
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(std::shared_ptr<Chunk> chunk, bool important) {
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found != meshes.end()){
if (chunk->isModified()) {
render(chunk, important);
}
return found->second;
}
return render(chunk, important);
}
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found != meshes.end()) {
return found->second;
}
return nullptr;
}
void ChunksRenderer::update() {
resultsMutex.lock();
while (!results.empty()) {
mesh_entry entry = results.front();
results.pop();
meshes[entry.key] = std::shared_ptr<Mesh>(entry.renderer.createMesh());
inwork.erase(entry.key);
entry.locked = false;
entry.variable.notify_all();
}
resultsMutex.unlock();
}
-67
View File
@@ -1,67 +0,0 @@
#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_
#define SRC_GRAPHICS_CHUNKSRENDERER_H_
#include <queue>
#include <mutex>
#include <thread>
#include <memory>
#include <vector>
#include <unordered_map>
#include <glm/glm.hpp>
#include <condition_variable>
#include "../../voxels/Block.h"
#include "../../voxels/ChunksStorage.h"
#include "../../settings.h"
class Mesh;
class Chunk;
class Level;
class BlocksRenderer;
class ContentGfxCache;
struct mesh_entry {
BlocksRenderer& renderer;
std::condition_variable& variable;
int workerIndex;
bool& locked;
glm::ivec2 key;
};
class ChunksRenderer {
std::unique_ptr<BlocksRenderer> renderer;
Level* level;
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
std::unordered_map<glm::ivec2, bool> inwork;
std::vector<std::thread> threads;
std::queue<mesh_entry> results;
std::mutex resultsMutex;
std::queue<std::shared_ptr<Chunk>> jobs;
std::condition_variable jobsMutexCondition;
std::mutex jobsMutex;
bool working = true;
const ContentGfxCache* cache;
const EngineSettings& settings;
std::vector<std::unique_lock<std::mutex>> workersBlocked;
void threadLoop(int index);
void process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer);
public:
ChunksRenderer(Level* level,
const ContentGfxCache* cache,
const EngineSettings& settings);
virtual ~ChunksRenderer();
std::shared_ptr<Mesh> render(std::shared_ptr<Chunk> chunk, bool important);
void unload(Chunk* chunk);
std::shared_ptr<Mesh> getOrRender(std::shared_ptr<Chunk> chunk, bool important);
std::shared_ptr<Mesh> get(Chunk* chunk);
void update();
};
#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_

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