5#include <unordered_map>
10constexpr double kEps = 1e-8;
11constexpr double kDeg2Rad = 0.017453292519943295;
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]);
23 bake.primitiveIds.push_back(
id);
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}};
36 for (
int i = 0; i < 8; ++i) {
40 pushBox(bake, corners,
id);
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}; }
46 const std::string&
id) {
49 const double len = std::sqrt(
dir.x *
dir.x +
dir.z *
dir.z);
50 if (len <= kEps ||
thickness <= kEps)
return;
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);
64const std::unordered_map<std::string, CatalogEntry>&
table() {
65 static const std::unordered_map<std::string, CatalogEntry> kTable = {
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"}}}},
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"}}}},
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"}}}},
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"}}}},
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"}}}},
107 const auto it = table().find(std::string(catalogId));
108 if (it == table().
end())
return std::nullopt;
113 std::vector<std::string>
ids;
115 for (
const auto& [
id, _] : table())
ids.push_back(
id);
116 std::sort(
ids.begin(),
ids.end());
123 const double c = std::cos(yawDegrees * kDeg2Rad);
124 const double s = std::sin(yawDegrees * kDeg2Rad);
127 position.z + part.center.x *
s + part.center.z *
c};
128 pushOrientedBox(bake,
center, part.size, yawDegrees,
itemId + part.suffix);
136 const std::vector<const Node*>& openings) {
139 if (len <= kEps)
return;
149 std::vector<Span> spans;
150 for (
const Node* child : openings) {
152 const double halfT = (child->width * 0.5) / len;
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));
160 if (span.t1 > span.t0 + 1e-6 && span.oh > kEps) spans.push_back(span);
162 std::sort(spans.begin(), spans.end(), [](
const Span&
a,
const Span&
b) { return a.t0 < b.t0; });
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;
174 last.id +=
"+" + span.id;
176 merged.push_back(span);
181 auto emit = [&](
double t0,
double t1,
double bottom,
double top,
const std::string&
id) {
186 for (
const Span& opening : merged) {
188 const double head = opening.sill + opening.oh;
190 emit(opening.t0, opening.t1, head, wall.
height, wall.
id +
".lintel");
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");
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},
204 opening.id +
".reveal.jamb.l");
205 pushWallBox(bake, {p1.
x -
dir.x * reveal, p1.
z -
dir.z * reveal},
207 opening.id +
".reveal.jamb.r");
209 pushWallBox(bake, p0, p1,
elevation, wall.
thickness, opening.sill - std::min(0.03, opening.sill),
210 opening.sill, opening.id +
".reveal.sill");
212 if (head + kEps < wall.
height) {
214 opening.id +
".reveal.head");
std::array< float, 3 > position
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()
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.