载入中...
搜索中...
未找到
HouseComponentLibrary.cpp
浏览该文件的文档.
2
3#include "common/Json.h"
4
5#include <algorithm>
6#include <filesystem>
7#include <fstream>
8#include <sstream>
9
10namespace eve::housegen {
11namespace {
12
14
15SocketDirection parseDirection(const std::string &s, bool *ok) {
16 *ok = true;
17 if (s == "north") return SocketDirection::North;
18 if (s == "east") return SocketDirection::East;
19 if (s == "south") return SocketDirection::South;
20 if (s == "west") return SocketDirection::West;
21 if (s == "up") return SocketDirection::Up;
22 if (s == "down") return SocketDirection::Down;
23 *ok = false;
25}
26
27bool parseComponent(Value o, HouseComponent &out, std::string *error) {
28 if (!o.isObject()) { if (error) *error = "component must be an object"; return false; }
29 out.id = o.getString("id");
30 out.modelPath = o.getString("model");
31 out.category = o.getString("category");
32 out.width = o.getInt("width", 1);
33 out.depth = o.getInt("depth", 1);
34 out.height = o.getInt("height", 1);
35 out.weight = o.getInt("weight", 1);
36 out.tags = o.getStringArray("tags");
37 if (o.get("rotations").isArray()) out.rotations = o.getIntArray("rotations");
38
39 const Value sockets = o.get("sockets");
40 for (size_t i = 0; i < sockets.size(); ++i) {
41 const Value so = sockets.at(i);
42 bool ok = false;
44 s.direction = parseDirection(so.getString("direction"), &ok);
45 s.type = so.getString("type");
46 s.accepts = so.getStringArray("accepts");
47 if (!ok || s.type.empty()) { if (error) *error = "socket needs a valid direction and type"; return false; }
48 out.sockets.push_back(std::move(s));
49 }
50
51 const Value material = o.get("material");
52 if (material.isObject()) {
53 const Value color = material.get("baseColor");
54 if (color.isArray()) {
55 if (color.size() != 3 && color.size() != 4) {
56 if (error) *error = "material baseColor needs 3 or 4 values";
57 return false;
58 }
59 out.material.hasBaseColor = true;
60 out.material.baseColorR = color.at(0).asFloat();
61 out.material.baseColorG = color.at(1).asFloat();
62 out.material.baseColorB = color.at(2).asFloat();
63 out.material.baseColorA = color.size() == 4 ? color.at(3).asFloat() : 1.f;
64 }
65 out.material.baseColorTexture = material.getString("baseColorTexture");
66 out.material.normalTexture = material.getString("normalTexture");
67 out.material.heightTexture = material.getString("heightTexture");
68 if (material.has("metallic")) {
69 out.material.hasMetallic = true;
70 out.material.metallic = material.getFloat("metallic");
71 }
72 if (material.has("roughness")) {
73 out.material.hasRoughness = true;
74 out.material.roughness = material.getFloat("roughness");
75 }
76 out.material.parallaxScale = material.getFloat("parallaxScale", 0.f);
77 out.material.parallaxMinLayers = material.getFloat("parallaxMinLayers", 8.f);
78 out.material.parallaxMaxLayers = material.getFloat("parallaxMaxLayers", 32.f);
79 out.material.cellBombScale = material.getFloat("cellBombScale", 4.f);
80 out.material.cellBombStrength = material.getFloat("cellBombStrength", 0.f);
81 out.material.cellBombRotation = material.getFloat("cellBombRotation", 1.f);
82 }
83 return true;
84}
85
86} // namespace
87
89 if (c.id.empty() || c.modelPath.empty() || c.category.empty()) {
90 if (error) *error = "component needs id, model and category";
91 return false;
92 }
93 if (c.width < 1 || c.depth < 1 || c.height < 1 || c.weight < 1) {
94 if (error) *error = "component dimensions and weight must be positive";
95 return false;
96 }
97 for (int r : c.rotations) if (r < 0 || r >= 360 || r % 90 != 0) {
98 if (error) *error = "rotations must be cardinal degrees";
99 return false;
100 }
101 const auto inUnitRange = [](float v) { return v >= 0.f && v <= 1.f; };
102 if ((c.material.hasBaseColor &&
103 (!inUnitRange(c.material.baseColorR) || !inUnitRange(c.material.baseColorG) ||
104 !inUnitRange(c.material.baseColorB) || !inUnitRange(c.material.baseColorA))) ||
105 (c.material.hasMetallic && !inUnitRange(c.material.metallic)) ||
106 (c.material.hasRoughness && !inUnitRange(c.material.roughness)) ||
107 !inUnitRange(c.material.cellBombStrength) || !inUnitRange(c.material.cellBombRotation)) {
108 if (error) *error = "material values must be between 0 and 1";
109 return false;
110 }
111 if (c.material.parallaxScale < 0.f || c.material.parallaxScale > 0.2f ||
112 c.material.parallaxMinLayers < 1.f ||
113 c.material.parallaxMaxLayers < c.material.parallaxMinLayers ||
114 c.material.cellBombScale <= 0.f) {
115 if (error) *error = "material sampling parameters are invalid";
116 return false;
117 }
118 if (components_.contains(c.id)) { if (error) *error = "duplicate component id: " + c.id; return false; }
119 components_.emplace(c.id, c);
120 return true;
121}
122
123bool HouseComponentLibrary::loadFromJson(const std::string &json, std::string *error) {
125 if (!doc.valid()) return false;
126 const Value root = doc.root();
127 const Value values = root.isArray() ? root : root.get("components");
128 if (!values.isArray()) { if (error) *error = "expected components array"; return false; }
129 HouseComponentLibrary candidate;
130 for (size_t i = 0; i < values.size(); ++i) {
132 if (!parseComponent(values.at(i), c, error) || !candidate.registerComponent(c, error))
133 return false;
134 }
135 components_ = std::move(candidate.components_);
136 return true;
137}
138
139bool HouseComponentLibrary::loadFromFile(const std::string &filename, std::string *error) {
140 std::ifstream input(filename, std::ios::binary);
141 if (!input) {
142 if (error) *error = "cannot open component manifest: " + filename;
143 return false;
144 }
145 std::ostringstream json;
146 json << input.rdbuf();
147 HouseComponentLibrary candidate;
148 if (!candidate.loadFromJson(json.str(), error)) return false;
149 const std::filesystem::path assetRoot =
150 std::filesystem::absolute(std::filesystem::path(filename)).parent_path();
151 for (auto &[_, component] : candidate.components_) {
152 std::filesystem::path model(component.modelPath);
153 if (model.is_relative()) component.modelPath = (assetRoot / model).lexically_normal().string();
154 }
155 components_ = std::move(candidate.components_);
156 return true;
157}
158
159void HouseComponentLibrary::clear() { components_.clear(); }
160const HouseComponent *HouseComponentLibrary::find(const std::string &id) const { auto it = components_.find(id); return it == components_.end() ? nullptr : &it->second; }
161int HouseComponentLibrary::count() const { return int(components_.size()); }
162std::vector<std::string> HouseComponentLibrary::ids() const { std::vector<std::string> out; for (const auto &[id, _] : components_) out.push_back(id); std::sort(out.begin(), out.end()); return out; }
163std::vector<const HouseComponent *> HouseComponentLibrary::byCategory(const std::string &category, const std::string &style) const {
164 std::vector<const HouseComponent *> out;
165 for (const auto &[_, c] : components_) if (c.category == category && (style.empty() || std::find(c.tags.begin(), c.tags.end(), style) != c.tags.end())) out.push_back(&c);
166 if (out.empty() && !style.empty()) return byCategory(category, {});
167 std::sort(out.begin(), out.end(), [](auto *a, auto *b) { return a->id < b->id; });
168 return out;
169}
170
171} // namespace eve::housegen
std::map< std::string, Var > values
std::string error
uint32_t a
uint32_t b
uint32_t c
glm::mat4 model
Material * material
int v
image::ImageData::Colorf color
uint32_t s
Definition Weather.cpp:28
房屋组件注册表(按 id 索引,支持分类/风格查询)。
bool loadFromFile(const std::string &filename, std::string *error=nullptr)
bool loadFromJson(const std::string &json, std::string *error=nullptr)
从 JSON / 文件加载组件。
bool registerComponent(const HouseComponent &component, std::string *error=nullptr)
注册单个组件(id 重复会报错)。
std::vector< std::string > ids() const
全部组件 id。
const HouseComponent * find(const std::string &id) const
按 id 查询组件。
std::vector< const HouseComponent * > byCategory(const std::string &category, const std::string &style={}) const
按分类(可选风格)查询组件。
bool valid() const
Definition Json.h:111
static Document parse(const std::string &text, std::string *error=nullptr)
Definition Json.cpp:449
Value root() const
Definition Json.h:112
SocketDirection
组件连接点方向(上下 + 四向)。
房屋组件模板(几何 + 连接点 + 权重)。
int width
占地尺寸(格)。
std::vector< int > rotations
允许的旋转(度)。
std::vector< std::string > tags
HouseMaterialOverride material
材质覆盖。
std::vector< HouseSocket > sockets
连接点。
int weight
生成权重(越大越常被选中)。
组件连接点:方向 + 类型 + 可接受类型。