mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(gameplay): add creature manager for resource resolution
Introduce CreatureManager to load per-creature JSON definitions and centralize model, animation, and collision resource locations. Refactor model and hitbox managers to use CreatureManager lookups instead of hardcoded asset path patterns. Also update ResourceLocation asset path helpers to return std::filesystem::path and use shared JSON parsing utilities.
This commit is contained in:
40
include/Cubed/gameplay/creatures/creature_manager.hpp
Normal file
40
include/Cubed/gameplay/creatures/creature_manager.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "Cubed/tools/resource_location.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
|
||||
struct CreatureData {
|
||||
ResourceLocation name{};
|
||||
std::optional<ResourceLocation> model{};
|
||||
std::optional<ResourceLocation> animation{};
|
||||
std::optional<ResourceLocation> collision{};
|
||||
};
|
||||
|
||||
class CreatureManager {
|
||||
public:
|
||||
CreatureManager();
|
||||
~CreatureManager();
|
||||
|
||||
static CreatureManager& instance();
|
||||
void init();
|
||||
|
||||
const CreatureData& get_creature_data(std::string_view name) const;
|
||||
const CreatureData&
|
||||
get_creature_data(const ResourceLocation& location) const;
|
||||
|
||||
static const CreatureData& data(std::string_view name);
|
||||
static const CreatureData& data(const ResourceLocation& location);
|
||||
|
||||
private:
|
||||
using CreatureMap = tbb::concurrent_hash_map<ResourceLocation, CreatureData,
|
||||
ResourceLocation::Hash>;
|
||||
|
||||
using cacc = CreatureMap::const_accessor;
|
||||
using acc = CreatureMap::accessor;
|
||||
static inline const CreatureData EMPTY;
|
||||
CreatureMap m_creature_map;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -12,6 +12,8 @@ struct Hitbox {
|
||||
Hitbox(glm::vec3 center_point, glm::vec3 half_size)
|
||||
: center(center_point), half(half_size) {}
|
||||
|
||||
Hitbox() {};
|
||||
|
||||
glm::vec3 min() const { return center - half; }
|
||||
|
||||
glm::vec3 max() const { return center + half; }
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Cubed {
|
||||
class HitboxManager {
|
||||
public:
|
||||
struct Handle {
|
||||
Hitbox box;
|
||||
Hitbox box{};
|
||||
HitboxID id = 0;
|
||||
};
|
||||
HitboxManager();
|
||||
@@ -28,6 +28,7 @@ private:
|
||||
using HitboxMap = tbb::concurrent_hash_map<HitboxID, Hitbox>;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, HitboxID>;
|
||||
using NameMap = tbb::concurrent_hash_map<HitboxID, std::string>;
|
||||
|
||||
HitboxID m_next = 0;
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class CreatureData;
|
||||
class ModelManager {
|
||||
public:
|
||||
struct Handle {
|
||||
@@ -41,6 +42,6 @@ private:
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
Handle load_model(std::string_view model_name);
|
||||
void load_anim_config(ModelNode& node, const std::string& path);
|
||||
void load_anim_config(ModelNode& node, const CreatureData& data);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
@@ -28,7 +29,7 @@ struct ResourceLocation {
|
||||
std::string(str.substr(it + 1))};
|
||||
}
|
||||
|
||||
static std::string get_assets_path(std::string_view ns) {
|
||||
static std::filesystem::path get_assets_path_prefix(std::string_view ns) {
|
||||
if (ns == "cubed") {
|
||||
return ASSETS_PATH "cubed";
|
||||
}
|
||||
@@ -51,7 +52,9 @@ struct ResourceLocation {
|
||||
return std::hash<std::string>()(p.to_string());
|
||||
}
|
||||
|
||||
std::string assets_path() const { return get_assets_path(ns); }
|
||||
std::filesystem::path assets_path_prefix() const {
|
||||
return get_assets_path_prefix(ns);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -109,4 +109,5 @@ target_sources(${PROJECT_NAME}
|
||||
ui/screenshot_ui.cpp
|
||||
scene/screenshot_scene.cpp
|
||||
ui/scroll_view.cpp
|
||||
gameplay/creatures/creature_manager.cpp
|
||||
)
|
||||
@@ -109,8 +109,8 @@ float BlockManager::roughness(BlockType id) {
|
||||
}
|
||||
|
||||
void BlockManager::init() {
|
||||
fs::path root_path{
|
||||
ResourceLocation::get_assets_path(ResourceLocation::DEFAULT_NAMESPACE)};
|
||||
fs::path root_path{ResourceLocation::get_assets_path_prefix(
|
||||
ResourceLocation::DEFAULT_NAMESPACE)};
|
||||
fs::path block_path = root_path / "blocks";
|
||||
|
||||
fs::create_directories(block_path);
|
||||
|
||||
94
src/gameplay/creatures/creature_manager.cpp
Normal file
94
src/gameplay/creatures/creature_manager.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "Cubed/gameplay/creatures/creature_manager.hpp"
|
||||
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/json_utils.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <rapidjson/document.h>
|
||||
namespace fs = std::filesystem;
|
||||
using namespace rapidjson;
|
||||
namespace Cubed {
|
||||
CreatureManager::CreatureManager() {}
|
||||
CreatureManager::~CreatureManager() {}
|
||||
CreatureManager& CreatureManager::instance() {
|
||||
static CreatureManager inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void CreatureManager::init() {
|
||||
|
||||
fs::path root_path{ResourceLocation::get_assets_path_prefix(
|
||||
ResourceLocation::DEFAULT_NAMESPACE)};
|
||||
fs::path creature_path = root_path / "creatures";
|
||||
|
||||
fs::create_directories(creature_path);
|
||||
|
||||
for (auto& entry : fs::recursive_directory_iterator(creature_path)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.path().extension() != ".json") {
|
||||
continue;
|
||||
}
|
||||
|
||||
Document doc;
|
||||
if (!Tools::parse_json(doc, entry.path())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
if (!Tools::get_json_value(doc, "name", name)) {
|
||||
Logger::error("creature json {} doesn't have name",
|
||||
entry.path().string());
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureData data;
|
||||
auto n = ResourceLocation::parse(name);
|
||||
if (!n) {
|
||||
continue;
|
||||
}
|
||||
data.name = *n;
|
||||
std::string s;
|
||||
if (Tools::get_json_value(doc, "model", s)) {
|
||||
data.model = ResourceLocation::parse(s);
|
||||
}
|
||||
if (Tools::get_json_value(doc, "animation", s)) {
|
||||
data.animation = ResourceLocation::parse(s);
|
||||
}
|
||||
if (Tools::get_json_value(doc, "collision", s)) {
|
||||
data.collision = ResourceLocation::parse(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CreatureData&
|
||||
CreatureManager::get_creature_data(std::string_view name) const {
|
||||
auto l = ResourceLocation::parse(name);
|
||||
if (l) {
|
||||
return get_creature_data(*l);
|
||||
} else {
|
||||
Logger::error("Can't get creature {} data", name);
|
||||
ASSERT(false);
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
const CreatureData&
|
||||
CreatureManager::get_creature_data(const ResourceLocation& location) const {
|
||||
cacc c;
|
||||
if (m_creature_map.find(c, location)) {
|
||||
return c->second;
|
||||
}
|
||||
Logger::error("Can't get creature {} data", location.to_string());
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
const CreatureData& CreatureManager::data(std::string_view name) {
|
||||
return instance().get_creature_data(name);
|
||||
}
|
||||
const CreatureData& CreatureManager::data(const ResourceLocation& location) {
|
||||
return instance().get_creature_data(location);
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "Cubed/gameplay/hitbox_manager.hpp"
|
||||
|
||||
#include "Cubed/gameplay/creatures/creature_manager.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/json_utils.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/resource_location.hpp"
|
||||
|
||||
@@ -9,6 +11,11 @@
|
||||
namespace fs = std::filesystem;
|
||||
using namespace rapidjson;
|
||||
namespace Cubed {
|
||||
|
||||
namespace {
|
||||
const HitboxManager::Handle EMPTY{};
|
||||
}
|
||||
|
||||
HitboxManager::HitboxManager() { HitboxMap::accessor a; }
|
||||
|
||||
HitboxManager::~HitboxManager() {}
|
||||
@@ -57,33 +64,24 @@ HitboxManager::Handle HitboxManager::get_hitbox(const std::string& name) {
|
||||
}
|
||||
HitboxManager::Handle HitboxManager::load(std::string_view name) {
|
||||
|
||||
auto space = ResourceLocation::parse(name);
|
||||
if (!space) {
|
||||
Logger::error("Can't load hitbox {}", name);
|
||||
auto& location = CreatureManager::data(name);
|
||||
if (!location.collision) {
|
||||
Logger::error("Can't find {} collision.json", name);
|
||||
ASSERT(false);
|
||||
return EMPTY;
|
||||
}
|
||||
fs::path p;
|
||||
if (space->ns == "cubed") {
|
||||
p = std::format("{}model/creature/{}/collision.json", ASSETS_PATH,
|
||||
space->path);
|
||||
} else {
|
||||
p = std::format("{}/model/creature/{}/collision.json", space->ns,
|
||||
space->path);
|
||||
}
|
||||
fs::path p =
|
||||
location.collision->assets_path_prefix() / location.collision->path;
|
||||
|
||||
try {
|
||||
glm::vec3 center;
|
||||
glm::vec3 half;
|
||||
std::ifstream s{p};
|
||||
IStreamWrapper isw(s);
|
||||
Document doc;
|
||||
doc.ParseStream(isw);
|
||||
if (doc.HasParseError()) {
|
||||
auto code = doc.GetParseError();
|
||||
|
||||
throw std::runtime_error(
|
||||
std::format("Parse {} failed, error code {}", p.string(),
|
||||
static_cast<int>(code)));
|
||||
Document doc;
|
||||
if (!Tools::parse_json(doc, p)) {
|
||||
Logger::error("Can't parse hitbox {}", name);
|
||||
ASSERT(false);
|
||||
return EMPTY;
|
||||
}
|
||||
if (doc.HasMember("boxes")) {
|
||||
const Value& box = doc["boxes"];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "Cubed/render/model_manager.hpp"
|
||||
|
||||
#include "Cubed/gameplay/creatures/creature_manager.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/json_utils.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/resource_location.hpp"
|
||||
|
||||
@@ -68,24 +70,20 @@ const std::string& ModelManager::get_model_name(ModelID id) {
|
||||
|
||||
ModelManager::Handle ModelManager::load_model(std::string_view model_name) {
|
||||
|
||||
auto space = ResourceLocation::parse(model_name);
|
||||
if (!space) {
|
||||
Logger::error("Can't Parse Model name {}", model_name);
|
||||
auto& location = CreatureManager::data(model_name);
|
||||
if (!location.model) {
|
||||
Logger::error("Can't find {} model key", model_name);
|
||||
ASSERT(false);
|
||||
}
|
||||
std::string path;
|
||||
if (space->ns == "cubed") {
|
||||
path = std::format("{}model/creature/{}/{}.glb", ASSETS_PATH,
|
||||
space->path, space->path);
|
||||
} else {
|
||||
path = std::format("./{}/model/creature/{}/{}.glb", space->ns,
|
||||
space->path, space->path);
|
||||
}
|
||||
|
||||
fs::path path = location.model->assets_path_prefix() / location.model->path;
|
||||
|
||||
auto model = m_loader.load(path);
|
||||
fs::path anim_path = path;
|
||||
anim_path = anim_path.parent_path() / "animation.json";
|
||||
load_anim_config(model, anim_path.string());
|
||||
|
||||
load_anim_config(model, location);
|
||||
|
||||
ModelMap::accessor acc;
|
||||
|
||||
if (m_models.insert(acc, m_next++)) {
|
||||
acc->second = std::move(model);
|
||||
} else {
|
||||
@@ -95,26 +93,26 @@ ModelManager::Handle ModelManager::load_model(std::string_view model_name) {
|
||||
return {cacc->second, cacc->first};
|
||||
}
|
||||
}
|
||||
|
||||
m_id_map.emplace(model_name, acc->first);
|
||||
m_name_map.emplace(acc->first, model_name);
|
||||
|
||||
return {acc->second, acc->first};
|
||||
}
|
||||
|
||||
void ModelManager::load_anim_config(ModelNode& node, const std::string& path) {
|
||||
if (!fs::is_regular_file(path)) {
|
||||
void ModelManager::load_anim_config(ModelNode& node, const CreatureData& data) {
|
||||
if (!data.animation) {
|
||||
return;
|
||||
}
|
||||
std::ifstream s(path);
|
||||
if (!s.is_open()) {
|
||||
return;
|
||||
}
|
||||
rapidjson::IStreamWrapper isw(s);
|
||||
|
||||
fs::path path = data.animation->assets_path_prefix() / data.animation->path;
|
||||
|
||||
rapidjson::Document doc;
|
||||
doc.ParseStream(isw);
|
||||
if (doc.HasParseError()) {
|
||||
Logger::warn("Can't parse anim config {}", path);
|
||||
|
||||
if (!Tools::parse_json(doc, path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModelAnimConfig cfg;
|
||||
if (doc.HasMember("walk")) {
|
||||
cfg.walk_speed = doc["walk"]["speed"].GetFloat();
|
||||
|
||||
Reference in New Issue
Block a user