载入中...
搜索中...
未找到
ArchSpaceCatalog.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <unordered_map>
6
7namespace eve::archspace {
8namespace {
9
10constexpr double kEps = 1e-8;
11constexpr double kDeg2Rad = 0.017453292519943295;
12
13void pushBox(MeshBake& bake, const Vec3 corners[8], const std::string& id) {
14 const std::uint32_t base = static_cast<std::uint32_t>(bake.positions.size());
15 for (int i = 0; i < 8; ++i) bake.positions.push_back(corners[i]);
16 static constexpr std::uint32_t faces[12][3] = {{0, 1, 2}, {0, 2, 3}, {4, 6, 5}, {4, 7, 6}, {0, 3, 7}, {0, 7, 4},
17 {1, 5, 6}, {1, 6, 2}, {0, 4, 5}, {0, 5, 1}, {3, 2, 6}, {3, 6, 7}};
18 for (const auto& f : faces) {
19 bake.indices.push_back(base + f[0]);
20 bake.indices.push_back(base + f[1]);
21 bake.indices.push_back(base + f[2]);
22 }
23 bake.primitiveIds.push_back(id);
24}
25
26void pushOrientedBox(MeshBake& bake, Vec3 center, Vec3 size, double yawDegrees, const std::string& id) {
27 if (size.x <= kEps || size.y <= kEps || size.z <= kEps) return;
28 const double c = std::cos(yawDegrees * kDeg2Rad);
29 const double s = std::sin(yawDegrees * kDeg2Rad);
30 const double hx = size.x * 0.5;
31 const double hy = size.y * 0.5;
32 const double hz = size.z * 0.5;
33 const Vec3 local[8] = {{-hx, -hy, -hz}, {hx, -hy, -hz}, {hx, -hy, hz}, {-hx, -hy, hz},
34 {-hx, hy, -hz}, {hx, hy, -hz}, {hx, hy, hz}, {-hx, hy, hz}};
35 Vec3 corners[8];
36 for (int i = 0; i < 8; ++i) {
37 corners[i] = {center.x + local[i].x * c - local[i].z * s, center.y + local[i].y,
38 center.z + local[i].x * s + local[i].z * c};
39 }
40 pushBox(bake, corners, id);
41}
42
43Vec2 mix(const Vec2& a, const Vec2& b, double t) { return {a.x + (b.x - a.x) * t, a.z + (b.z - a.z) * t}; }
44
45void pushWallBox(MeshBake& bake, Vec2 start, Vec2 end, double elevation, double thickness, double bottom, double top,
46 const std::string& id) {
47 if (top <= bottom + kEps) return;
48 Vec2 dir{end.x - start.x, end.z - start.z};
49 const double len = std::sqrt(dir.x * dir.x + dir.z * dir.z);
50 if (len <= kEps || thickness <= kEps) return;
51 dir.x /= len;
52 dir.z /= len;
53 const Vec2 n{-dir.z * thickness * 0.5, dir.x * thickness * 0.5};
54 const double h = top - bottom;
55 const Vec3 a{start.x, elevation + bottom, start.z};
56 const Vec3 b{end.x, elevation + bottom, end.z};
57 const Vec3 corners[8] = {{a.x + n.x, a.y, a.z + n.z}, {a.x - n.x, a.y, a.z - n.z},
58 {b.x - n.x, a.y, b.z - n.z}, {b.x + n.x, a.y, b.z + n.z},
59 {a.x + n.x, a.y + h, a.z + n.z}, {a.x - n.x, a.y + h, a.z - n.z},
60 {b.x - n.x, a.y + h, b.z - n.z}, {b.x + n.x, a.y + h, b.z + n.z}};
61 pushBox(bake, corners, id);
62}
63
64const std::unordered_map<std::string, CatalogEntry>& table() {
65 static const std::unordered_map<std::string, CatalogEntry> kTable = {
66 {"furniture.desk",
67 {"furniture.desk",
68 "Desk",
69 {{{0.0, 0.725, 0.0}, {1.40, 0.05, 0.70}, ".top"},
70 {{-0.60, 0.35, -0.28}, {0.08, 0.70, 0.08}, ".leg.fl"},
71 {{0.60, 0.35, -0.28}, {0.08, 0.70, 0.08}, ".leg.fr"},
72 {{-0.60, 0.35, 0.28}, {0.08, 0.70, 0.08}, ".leg.bl"},
73 {{0.60, 0.35, 0.28}, {0.08, 0.70, 0.08}, ".leg.br"}}}},
74 {"furniture.sofa",
75 {"furniture.sofa",
76 "Sofa",
77 {{{0.0, 0.22, 0.0}, {2.00, 0.44, 0.85}, ".seat"},
78 {{0.0, 0.55, -0.32}, {2.00, 0.50, 0.20}, ".back"},
79 {{-0.90, 0.40, 0.05}, {0.18, 0.40, 0.75}, ".arm.l"},
80 {{0.90, 0.40, 0.05}, {0.18, 0.40, 0.75}, ".arm.r"}}}},
81 {"furniture.bed",
82 {"furniture.bed",
83 "Bed",
84 {{{0.0, 0.28, 0.0}, {2.00, 0.36, 1.60}, ".mattress"},
85 {{0.0, 0.55, -0.72}, {2.00, 0.55, 0.12}, ".headboard"}}}},
86 {"furniture.chair",
87 {"furniture.chair",
88 "Chair",
89 {{{0.0, 0.45, 0.0}, {0.45, 0.08, 0.45}, ".seat"},
90 {{0.0, 0.70, -0.18}, {0.45, 0.45, 0.08}, ".back"},
91 {{-0.16, 0.22, -0.16}, {0.06, 0.44, 0.06}, ".leg.fl"},
92 {{0.16, 0.22, -0.16}, {0.06, 0.44, 0.06}, ".leg.fr"},
93 {{-0.16, 0.22, 0.16}, {0.06, 0.44, 0.06}, ".leg.bl"},
94 {{0.16, 0.22, 0.16}, {0.06, 0.44, 0.06}, ".leg.br"}}}},
95 {"furniture.table",
96 {"furniture.table",
97 "Table",
98 {{{0.0, 0.72, 0.0}, {1.20, 0.06, 1.20}, ".top"}, {{0.0, 0.36, 0.0}, {0.12, 0.72, 0.12}, ".pedestal"}}}},
99 {"furniture.wardrobe", {"furniture.wardrobe", "Wardrobe", {{{0.0, 1.05, 0.0}, {1.20, 2.10, 0.60}, ".body"}}}},
100 };
101 return kTable;
102}
103
104} // namespace
105
106std::optional<CatalogEntry> lookupCatalog(std::string_view catalogId) {
107 const auto it = table().find(std::string(catalogId));
108 if (it == table().end()) return std::nullopt;
109 return it->second;
110}
111
112std::vector<std::string> listCatalogIds() {
113 std::vector<std::string> ids;
114 ids.reserve(table().size());
115 for (const auto& [id, _] : table()) ids.push_back(id);
116 std::sort(ids.begin(), ids.end());
117 return ids;
118}
119
120void appendCatalogItem(MeshBake& bake, const std::string& itemId, const std::string& catalogId, const Vec3& position,
121 double yawDegrees) {
122 if (const auto entry = lookupCatalog(catalogId)) {
123 const double c = std::cos(yawDegrees * kDeg2Rad);
124 const double s = std::sin(yawDegrees * kDeg2Rad);
125 for (const CatalogPart& part : entry->parts) {
126 const Vec3 center{position.x + part.center.x * c - part.center.z * s, position.y + part.center.y,
127 position.z + part.center.x * s + part.center.z * c};
128 pushOrientedBox(bake, center, part.size, yawDegrees, itemId + part.suffix);
129 }
130 return;
131 }
132 pushOrientedBox(bake, {position.x, position.y + 0.45, position.z}, {0.50, 0.90, 0.50}, yawDegrees, itemId);
133}
134
135void appendWallWithOpenings(MeshBake& bake, const Node& wall, double elevation,
136 const std::vector<const Node*>& openings) {
137 const double len = std::sqrt((wall.end.x - wall.start.x) * (wall.end.x - wall.start.x) +
138 (wall.end.z - wall.start.z) * (wall.end.z - wall.start.z));
139 if (len <= kEps) return;
140
141 struct Span {
142 double t0 = 0;
143 double t1 = 0;
144 OpeningKind kind = OpeningKind::Door;
145 double sill = 0;
146 double oh = 0;
147 std::string id;
148 };
149 std::vector<Span> spans;
150 for (const Node* child : openings) {
151 if (!child || child->kind != NodeKind::Opening) continue;
152 const double halfT = (child->width * 0.5) / len;
153 Span span;
154 span.t0 = std::clamp(child->t - halfT, 0.0, 1.0);
155 span.t1 = std::clamp(child->t + halfT, 0.0, 1.0);
156 span.kind = child->openingKind;
157 span.sill = std::clamp(child->sill, 0.0, wall.height);
158 span.oh = std::clamp(child->openingHeight, 0.0, std::max(0.0, wall.height - span.sill));
159 span.id = child->id;
160 if (span.t1 > span.t0 + 1e-6 && span.oh > kEps) spans.push_back(span);
161 }
162 std::sort(spans.begin(), spans.end(), [](const Span& a, const Span& b) { return a.t0 < b.t0; });
163
164 std::vector<Span> merged;
165 for (const Span& span : spans) {
166 if (!merged.empty() && span.t0 <= merged.back().t1 + 1e-6) {
167 Span& last = merged.back();
168 last.t1 = std::max(last.t1, span.t1);
169 const double lastTop = last.sill + last.oh;
170 const double spanTop = span.sill + span.oh;
171 last.sill = std::min(last.sill, span.sill);
172 last.oh = std::max(lastTop, spanTop) - last.sill;
173 if (span.kind == OpeningKind::Door) last.kind = OpeningKind::Door;
174 last.id += "+" + span.id;
175 } else {
176 merged.push_back(span);
177 }
178 }
179
180 double cursor = 0.0;
181 auto emit = [&](double t0, double t1, double bottom, double top, const std::string& id) {
182 pushWallBox(bake, mix(wall.start, wall.end, t0), mix(wall.start, wall.end, t1), elevation, wall.thickness,
183 bottom, top, id);
184 };
185
186 for (const Span& opening : merged) {
187 emit(cursor, opening.t0, 0.0, wall.height, wall.id);
188 const double head = opening.sill + opening.oh;
189 if (opening.kind == OpeningKind::Door) {
190 emit(opening.t0, opening.t1, head, wall.height, wall.id + ".lintel");
191 } else {
192 emit(opening.t0, opening.t1, 0.0, opening.sill, wall.id + ".sill");
193 emit(opening.t0, opening.t1, head, wall.height, wall.id + ".head");
194 }
195
196 const Vec2 p0 = mix(wall.start, wall.end, opening.t0);
197 const Vec2 p1 = mix(wall.start, wall.end, opening.t1);
198 Vec2 dir{wall.end.x - wall.start.x, wall.end.z - wall.start.z};
199 dir.x /= len;
200 dir.z /= len;
201 const double reveal = std::min(0.04, wall.thickness * 0.5);
202 pushWallBox(bake, {p0.x - dir.x * reveal, p0.z - dir.z * reveal},
203 {p0.x + dir.x * reveal, p0.z + dir.z * reveal}, elevation, wall.thickness, opening.sill, head,
204 opening.id + ".reveal.jamb.l");
205 pushWallBox(bake, {p1.x - dir.x * reveal, p1.z - dir.z * reveal},
206 {p1.x + dir.x * reveal, p1.z + dir.z * reveal}, elevation, wall.thickness, opening.sill, head,
207 opening.id + ".reveal.jamb.r");
208 if (opening.kind == OpeningKind::Window && opening.sill > kEps) {
209 pushWallBox(bake, p0, p1, elevation, wall.thickness, opening.sill - std::min(0.03, opening.sill),
210 opening.sill, opening.id + ".reveal.sill");
211 }
212 if (head + kEps < wall.height) {
213 pushWallBox(bake, p0, p1, elevation, wall.thickness, head, head + std::min(0.03, wall.height - head),
214 opening.id + ".reveal.head");
215 }
216 cursor = std::max(cursor, opening.t1);
217 }
218 emit(cursor, 1.0, 0.0, wall.height, wall.id);
219}
220
221} // namespace eve::archspace
Duration start
LogicalId itemId
SQInteger top
const std::string & s
float thickness
Definition CaveMesh.cpp:83
glm::uvec4 ids
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
float elevation
int h
TokenKind kind
std::string local
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float f
std::string id
Definition PlayHost.cpp:108
float t
std::size_t cursor
float size
Definition TreeMesh.cpp:156
V3 dir
Definition TreeMesh.cpp:150
float bottom
std::optional< CatalogEntry > lookupCatalog(std::string_view catalogId)
Resolve a built-in furniture catalog entry.
OpeningKind
Opening semantic used by doors and windows hosted on walls.
void appendWallWithOpenings(MeshBake &bake, const Node &wall, double elevation, const std::vector< const Node * > &openings)
Bake one wall with merged opening cavities and explicit reveal liners.
std::vector< std::string > listCatalogIds()
Stable list of built-in catalog ids for tools and inspector UIs.
void appendCatalogItem(MeshBake &bake, const std::string &itemId, const std::string &catalogId, const Vec3 &position, double yawDegrees)
Append yaw-oriented catalog geometry into a mesh bake.
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
One parametric furniture part in catalog-local metres (Y-up).
Renderer-neutral triangle mesh rebuilt from the document.
One authored architectural node. Only fields for the active kind are meaningful.
Metres on the level XZ plane.
Metres in world/level space.
std::string id
Definition Widget.h:17