add EnumMetadata

This commit is contained in:
MihailRis 2025-04-13 13:53:59 +03:00
parent d888ddec4c
commit 7749675a61
20 changed files with 156 additions and 206 deletions

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "GLSLExtension.hpp"
#include <sstream>
@ -95,22 +96,6 @@ inline void source_line(std::stringstream& ss, uint linenum) {
ss << "#line " << linenum << "\n";
}
static std::optional<Type> param_type_from(
const std::string& name
) {
static const std::unordered_map<std::string, Type> typeNames {
{"float", Type::FLOAT},
{"vec2", Type::VEC2},
{"vec3", Type::VEC3},
{"vec4", Type::VEC4},
};
const auto& found = typeNames.find(name);
if (found == typeNames.end()) {
return std::nullopt;
}
return found->second;
}
static Value default_value_for(Type type) {
switch (type) {
case Type::FLOAT:
@ -212,11 +197,13 @@ public:
}
bool processParamDirective() {
using Param = PostEffect::Param;
skipWhitespace(false);
// Parse type name
auto typeName = parseName();
auto type = param_type_from(typeName);
if (!type.has_value()) {
Param::Type type;
if (!Param::TypeMeta.getItem(typeName, type)) {
throw error("unsupported param type " + util::quote(typeName));
}
skipWhitespace(false);
@ -228,17 +215,17 @@ public:
skipWhitespace(false);
ss << "uniform " << typeName << " " << paramName << ";\n";
auto defValue = default_value_for(type.value());
auto defValue = default_value_for(type);
// Parse default value
if (peekNoJump() == '=') {
skip(1);
skipWhitespace(false);
defValue = parseDefaultValue(type.value(), typeName);
defValue = parseDefaultValue(type, typeName);
}
skipLine();
params[paramName] = PostEffect::Param(type.value(), std::move(defValue));
params[paramName] = PostEffect::Param(type, std::move(defValue));
return false;
}

View File

@ -1,7 +1,6 @@
#pragma once
#include <memory>
#include <optional>
#include <set>
#include <stdexcept>
#include <string>
@ -25,23 +24,6 @@ namespace rigging {
class SkeletonConfig;
}
constexpr const char* ContentType_name(ContentType type) {
switch (type) {
case ContentType::NONE:
return "none";
case ContentType::BLOCK:
return "block";
case ContentType::ITEM:
return "item";
case ContentType::ENTITY:
return "entity";
case ContentType::GENERATOR:
return "generator";
default:
return "unknown";
}
}
class namereuse_error : public std::runtime_error {
ContentType type;
public:
@ -185,26 +167,6 @@ public:
}
};
constexpr const char* to_string(ResourceType type) {
switch (type) {
case ResourceType::CAMERA:
return "camera";
case ResourceType::POST_EFFECT_SLOT:
return "post-effect-slot";
default:
return "unknown";
}
}
inline std::optional<ResourceType> ResourceType_from(std::string_view str) {
if (str == "camera") {
return ResourceType::CAMERA;
} else if (str == "post-effect-slot") {
return ResourceType::POST_EFFECT_SLOT;
}
return std::nullopt;
}
using ResourceIndicesSet = ResourceIndices[RESOURCE_TYPES_COUNT];
/// @brief Content is a definitions repository

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "ContentLoader.hpp"
#include <algorithm>
@ -259,28 +260,25 @@ void ContentLoader::loadBlock(
}
// block model
std::string modelTypeName = to_string(def.model);
std::string modelTypeName = BlockModelMeta.getNameString(def.model);
root.at("model").get(modelTypeName);
root.at("model-name").get(def.modelName);
if (auto model = BlockModel_from(modelTypeName)) {
if (*model == BlockModel::custom && def.customModelRaw == nullptr) {
if (BlockModelMeta.getItem(modelTypeName, def.model)) {
if (def.model == BlockModel::custom && def.customModelRaw == nullptr) {
if (root.has("model-primitives")) {
def.customModelRaw = root["model-primitives"];
} else if (def.modelName.empty()) {
throw std::runtime_error(name + ": no 'model-primitives' or 'model-name' found");
}
}
def.model = *model;
} else if (!modelTypeName.empty()) {
logger.error() << "unknown model: " << modelTypeName;
def.model = BlockModel::none;
}
std::string cullingModeName = to_string(def.culling);
std::string cullingModeName = CullingModeMeta.getNameString(def.culling);
root.at("culling").get(cullingModeName);
if (auto mode = CullingMode_from(cullingModeName)) {
def.culling = *mode;
} else {
if (!CullingModeMeta.getItem(cullingModeName, def.culling)) {
logger.error() << "unknown culling mode: " << cullingModeName;
}
@ -518,9 +516,7 @@ void ContentLoader::loadEntity(
std::string bodyTypeName;
root.at("body-type").get(bodyTypeName);
if (auto bodyType = BodyType_from(bodyTypeName)) {
def.bodyType = *bodyType;
}
BodyTypeMeta.getItem(bodyTypeName, def.bodyType);
root.at("skeleton-name").get(def.skeletonName);
root.at("blocking").get(def.blocking);
@ -799,8 +795,9 @@ void ContentLoader::load() {
if (io::exists(resourcesFile)) {
auto resRoot = io::read_json(resourcesFile);
for (const auto& [key, arr] : resRoot.asObject()) {
if (auto resType = ResourceType_from(key)) {
loadResources(*resType, arr);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
loadResources(type, arr);
} else {
// Ignore unknown resources
logger.warning() << "unknown resource type: " << key;
@ -813,8 +810,9 @@ void ContentLoader::load() {
if (io::exists(aliasesFile)) {
auto resRoot = io::read_json(aliasesFile);
for (const auto& [key, arr] : resRoot.asObject()) {
if (auto resType = ResourceType_from(key)) {
loadResourceAliases(*resType, arr);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
loadResourceAliases(type, arr);
} else {
// Ignore unknown resources
logger.warning() << "unknown resource type: " << key;

View File

@ -1,12 +1,21 @@
#pragma once
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
class Content;
class ContentPackRuntime;
enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR };
VC_ENUM_METADATA(ContentType)
{"none", ContentType::NONE},
{"block", ContentType::BLOCK},
{"item", ContentType::ITEM},
{"entity", ContentType::ENTITY},
{"generator", ContentType::GENERATOR},
VC_ENUM_END
enum class ResourceType : size_t {
CAMERA,
POST_EFFECT_SLOT,
@ -15,3 +24,8 @@ enum class ResourceType : size_t {
inline constexpr auto RESOURCE_TYPES_COUNT =
static_cast<size_t>(ResourceType::LAST) + 1;
VC_ENUM_METADATA(ResourceType)
{"camera", ResourceType::CAMERA},
{"post-effect-slot", ResourceType::POST_EFFECT_SLOT},
VC_ENUM_END

View File

@ -4,7 +4,6 @@
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <optional>
#include "typedefs.hpp"
#include "interfaces/Serializable.hpp"

View File

@ -7,6 +7,7 @@
#include <glm/glm.hpp>
#include "data/dv_fwd.hpp"
#include "util/EnumMetadata.hpp"
class Shader;
@ -14,6 +15,14 @@ class PostEffect {
public:
struct Param {
enum class Type { FLOAT, VEC2, VEC3, VEC4 };
VC_ENUM_METADATA(Type)
{"float", Type::FLOAT},
{"vec2", Type::VEC2},
{"vec3", Type::VEC3},
{"vec4", Type::VEC4},
VC_ENUM_END
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>;
Type type;

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "EngineController.hpp"
#include <algorithm>
@ -164,7 +165,7 @@ static dv::value create_missing_content_report(
auto root = dv::object();
auto& contentEntries = root.list("content");
for (auto& entry : report->getMissingContent()) {
std::string contentName = ContentType_name(entry.type);
std::string contentName = ContentTypeMeta.getNameString(entry.type);
auto& contentEntry = contentEntries.object();
contentEntry["type"] = contentName;
contentEntry["name"] = entry.name;

View File

@ -1,3 +1,5 @@
#define VC_ENABLE_REFLECTION
#include "util/stringutil.hpp"
#include "libentity.hpp"
@ -96,8 +98,8 @@ static int l_set_crouching(lua::State* L) {
static int l_get_body_type(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
return lua::pushstring(
L, to_string(entity->getRigidbody().hitbox.type)
return lua::pushlstring(
L, BodyTypeMeta.getName(entity->getRigidbody().hitbox.type)
);
}
return 0;
@ -105,9 +107,9 @@ static int l_get_body_type(lua::State* L) {
static int l_set_body_type(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
if (auto type = BodyType_from(lua::tostring(L, 2))) {
entity->getRigidbody().hitbox.type = *type;
} else {
if (!BodyTypeMeta.getItem(
lua::tostring(L, 2), entity->getRigidbody().hitbox.type
)) {
throw std::runtime_error(
"unknown body type " + util::quote(lua::tostring(L, 2))
);

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "content/Content.hpp"
#include "content/ContentLoader.hpp"
#include "content/ContentControl.hpp"
@ -311,7 +312,7 @@ static int l_get_textures(lua::State* L) {
static int l_get_model(lua::State* L) {
if (auto def = require_block(L)) {
return lua::pushstring(L, to_string(def->model));
return lua::pushlstring(L, BlockModelMeta.getName(def->model));
}
return 0;
}

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "Entities.hpp"
#include <glm/ext/matrix_transform.hpp>
@ -216,9 +217,7 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
dv::get_vec(bodymap, "vel", body.hitbox.velocity);
std::string bodyTypeName;
map.at("type").get(bodyTypeName);
if (auto bodyType = BodyType_from(bodyTypeName)) {
body.hitbox.type = *bodyType;
}
BodyTypeMeta.getItem(bodyTypeName, body.hitbox.type);
bodymap["crouch"].asBoolean(body.hitbox.crouching);
bodymap["damping"].asNumber(body.hitbox.linearDamping);
}
@ -329,7 +328,7 @@ dv::value Entities::serialize(const Entity& entity) {
if (def.save.body.settings) {
bodymap["damping"] = rigidbody.hitbox.linearDamping;
if (hitbox.type != def.bodyType) {
bodymap["type"] = to_string(hitbox.type);
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
}
if (hitbox.crouching) {
bodymap["crouch"] = hitbox.crouching;

View File

@ -2,7 +2,6 @@
#include <glm/glm.hpp>
#include <memory>
#include <optional>
#include "interfaces/Serializable.hpp"
#include "settings.hpp"

View File

@ -2,26 +2,6 @@
#include <stdexcept>
std::optional<BodyType> BodyType_from(std::string_view str) {
if (str == "kinematic") {
return BodyType::KINEMATIC;
} else if (str == "dynamic") {
return BodyType::DYNAMIC;
} else if (str == "static") {
return BodyType::STATIC;
}
return std::nullopt;
}
std::string to_string(BodyType type) {
switch (type) {
case BodyType::KINEMATIC: return "kinematic";
case BodyType::DYNAMIC: return "dynamic";
case BodyType::STATIC: return "static";
default: return "unknown";
}
}
Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
: type(type),
position(position),

View File

@ -2,10 +2,10 @@
#include "maths/aabb.hpp"
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
#include <set>
#include <string>
#include <optional>
#include <functional>
#include <glm/glm.hpp>
@ -41,8 +41,11 @@ enum class BodyType {
STATIC, KINEMATIC, DYNAMIC
};
std::optional<BodyType> BodyType_from(std::string_view str);
std::string to_string(BodyType type);
VC_ENUM_METADATA(BodyType)
{"static", BodyType::STATIC},
{"kinematic", BodyType::KINEMATIC},
{"dynamic", BodyType::DYNAMIC},
VC_ENUM_END
struct Hitbox {
BodyType type;

View File

@ -1,37 +1,13 @@
#define VC_ENABLE_REFLECTION
#include "NotePreset.hpp"
#include <map>
#include <vector>
#include "data/dv_util.hpp"
std::string to_string(NoteDisplayMode mode) {
static std::vector<std::string> names = {
"static_billboard",
"y_free_billboard",
"xy_free_billboard",
"projected"
};
return names.at(static_cast<int>(mode));
}
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s) {
static std::map<std::string_view, NoteDisplayMode, std::less<>> map {
{"static_billboard", NoteDisplayMode::STATIC_BILLBOARD},
{"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD},
{"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD},
{"projected", NoteDisplayMode::PROJECTED}
};
const auto& found = map.find(s);
if (found == map.end()) {
return std::nullopt;
}
return found->second;
}
#include <vector>
dv::value NotePreset::serialize() const {
return dv::object({
{"display", to_string(displayMode)},
{"display", NoteDisplayModeMeta.getNameString(displayMode)},
{"color", dv::to_value(color)},
{"scale", scale},
{"render_distance", renderDistance},
@ -42,7 +18,7 @@ dv::value NotePreset::serialize() const {
void NotePreset::deserialize(const dv::value& src) {
if (src.has("display")) {
displayMode = NoteDisplayMode_from(src["display"].asString()).value();
NoteDisplayModeMeta.getItem(src["display"].asString(), displayMode);
}
if (src.has("color")) {
dv::get_vec(src["color"], color);

View File

@ -2,10 +2,10 @@
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <optional>
#include <string>
#include "interfaces/Serializable.hpp"
#include "util/EnumMetadata.hpp"
enum class NoteDisplayMode {
STATIC_BILLBOARD,
@ -14,8 +14,12 @@ enum class NoteDisplayMode {
PROJECTED
};
std::string to_string(NoteDisplayMode mode);
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s);
VC_ENUM_METADATA(NoteDisplayMode)
{"static_billboard", NoteDisplayMode::STATIC_BILLBOARD},
{"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD},
{"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD},
{"projected", NoteDisplayMode::PROJECTED},
VC_ENUM_END
struct NotePreset : public Serializable {
NoteDisplayMode displayMode = NoteDisplayMode::STATIC_BILLBOARD;

61
src/util/EnumMetadata.hpp Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#ifdef VC_ENABLE_REFLECTION
#define VC_ENUM_METADATA(NAME) static inline util::EnumMetadata<NAME> NAME##Meta {
#define VC_ENUM_END };
#include <map>
#include <string>
#include <utility>
namespace util {
template<typename EnumT>
class EnumMetadata {
public:
EnumMetadata(
std::initializer_list<std::pair<const std::string_view, EnumT>> items
)
: items(items) {
for (const auto& [name, item] : items) {
names[item] = name;
}
}
std::string_view getName(EnumT item) const {
const auto& found = names.find(item);
if (found == names.end()) {
return "";
}
return found->second;
}
std::string getNameString(EnumT item) const {
return std::string(getName(item));
}
bool getItem(std::string_view name, EnumT& dst) const {
const auto& found = items.find(name);
if (found == items.end()) {
return false;
}
dst = found->second;
return true;
}
size_t size() const {
return items.size();
}
private:
std::map<std::string_view, EnumT> items;
std::map<EnumT, std::string_view> names;
};
}
#else
#include <initializer_list>
#define VC_ENUM_METADATA(NAME) \
struct NAME##__PAIR {const char* n; NAME i;}; \
[[maybe_unused]] static inline std::initializer_list<NAME##__PAIR> NAME##_PAIRS {
#define VC_ENUM_END };
#endif // VC_ENABLE_REFLECTION

View File

@ -18,62 +18,6 @@ dv::value BlockMaterial::serialize() const {
});
}
std::string to_string(BlockModel model) {
switch (model) {
case BlockModel::none:
return "none";
case BlockModel::block:
return "block";
case BlockModel::xsprite:
return "X";
case BlockModel::aabb:
return "aabb";
case BlockModel::custom:
return "custom";
default:
return "unknown";
}
}
std::optional<BlockModel> BlockModel_from(std::string_view str) {
if (str == "none") {
return BlockModel::none;
} else if (str == "block") {
return BlockModel::block;
} else if (str == "X") {
return BlockModel::xsprite;
} else if (str == "aabb") {
return BlockModel::aabb;
} else if (str == "custom") {
return BlockModel::custom;
}
return std::nullopt;
}
std::string to_string(CullingMode mode) {
switch (mode) {
case CullingMode::DEFAULT:
return "default";
case CullingMode::OPTIONAL:
return "optional";
case CullingMode::DISABLED:
return "disabled";
default:
return "unknown";
}
}
std::optional<CullingMode> CullingMode_from(std::string_view str) {
if (str == "default") {
return CullingMode::DEFAULT;
} else if (str == "optional") {
return CullingMode::OPTIONAL;
} else if (str == "disabled") {
return CullingMode::DISABLED;
}
return std::nullopt;
}
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
: axes({axisX, axisY, axisZ}) {
fix = glm::ivec3(0);

View File

@ -1,7 +1,6 @@
#pragma once
#include <glm/glm.hpp>
#include <optional>
#include <string>
#include <vector>
#include <array>
@ -10,6 +9,7 @@
#include "maths/UVRegion.hpp"
#include "maths/aabb.hpp"
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
struct ParticlesPreset;
@ -93,8 +93,13 @@ enum class BlockModel {
custom
};
std::string to_string(BlockModel model);
std::optional<BlockModel> BlockModel_from(std::string_view str);
VC_ENUM_METADATA(BlockModel)
{"none", BlockModel::none},
{"block", BlockModel::block},
{"X", BlockModel::xsprite},
{"aabb", BlockModel::aabb},
{"custom", BlockModel::custom},
VC_ENUM_END
enum class CullingMode {
DEFAULT,
@ -102,8 +107,11 @@ enum class CullingMode {
DISABLED,
};
std::string to_string(CullingMode mode);
std::optional<CullingMode> CullingMode_from(std::string_view str);
VC_ENUM_METADATA(CullingMode)
{"default", CullingMode::DEFAULT},
{"optional", CullingMode::OPTIONAL},
{"disabled", CullingMode::DISABLED},
VC_ENUM_END
using BoxModel = AABB;

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "World.hpp"
#include <glm/glm.hpp>
@ -49,7 +50,7 @@ void World::updateTimers(float delta) {
void World::writeResources(const Content& content) {
auto root = dv::object();
for (size_t typeIndex = 0; typeIndex < RESOURCE_TYPES_COUNT; typeIndex++) {
auto typeName = to_string(static_cast<ResourceType>(typeIndex));
auto typeName = ResourceTypeMeta.getNameString(static_cast<ResourceType>(typeIndex));
auto& list = root.list(typeName);
auto& indices = content.resourceIndices[typeIndex];
for (size_t i = 0; i < indices.size(); i++) {

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "WorldFiles.hpp"
#include <cassert>
@ -182,8 +183,9 @@ bool WorldFiles::readResourcesData(const Content& content) {
}
auto root = io::read_json(file);
for (const auto& [key, arr] : root.asObject()) {
if (auto resType = ResourceType_from(key)) {
read_resources_data(content, arr, *resType);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
read_resources_data(content, arr, type);
} else {
logger.warning() << "unknown resource type: " << key;
}