把 maptext「一段文本 → 一座 3D 城市」的整套哲学,原封不动搬到室内尺度:外面那座城换成楼里面。
现在写几行文本,右边就长出一个可以走进去的 3D 室内空间——50 种主题场景、76 个房间原型、161 种家具元素,
带真楼梯、真中庭挑空、真门窗洞、真碰撞摆放。目前是独立页面,接口按 maptext 同一套设计,之后可直接整合。
| maptext(室外) | ROOMTEXT(室内) | 为什么必须一样 |
|---|---|---|
@grid 手画 ASCII 地图 | @plan 手画 ASCII 平面图(一个字符 = 一平米) | 小的想手画、大的画不动,两者都要 |
@zones/@rules 写规则让城长出来 | @program 写房间配比,递归二分把平面切出来 | 40×26 的航站楼没人愿意一格一格画 |
@legend 把"字符"和"长什么样"分开 | @legend + 房间原型 + 调色板三层分离 | 换一行图例 = 整座城/整栋楼换风格 |
hash(seed,x,z,k) 稳定随机 | 同一个 hash,同源实现 | 同一份文本永远长出同一个东西 |
| 楼房 = 底座+中段+冠顶+屋顶件 四段拼 | 房间 = 结构+大件家具+台面摆设+灯+软装 五层堆 | 少量零件组合出成百上千种不重样 |
接口也刻意保持一致:parse(文本) → 模型、build(模型) → 几何数据、材质写名字不写颜色。
之后整合时,maptext 的一栋楼只要挂一个 scene id + seed,走进门就能即时长出室内——
城市层负责"哪栋楼在哪",室内层负责"进门之后是什么"。
`
【场景 SCENE】 50 个 —— 一栋楼的性格(咖啡厅 / 夜店 / 教室 / 机场…)
↓ 说"我由哪些房间按什么比例组成" @program
【房间原型 ARCH】76 个 —— 一个房间的性格(开放办公区 / 吧台 / 舞池…)
↓ 说"我里面摆什么、怎么摆"(写规则不是写坐标) fill
【元素 PROP】 161 种 —— 一件家具 / 灯 / 摆设,每件由 4-20 个盒子搭成
`
为什么必须拆三层:50 个场景如果各写各的家具坐标,就是 50 份硬编码,改一次要改 50 遍。
拆开之后——咖啡厅和面包店共用同一个吧台原型;想让全世界的会议室都多一块白板,只改一个原型;
想把夜店变成粉色夜店,只换调色板,一条摆放规则都不用动。
| 分类 | 场景 |
|---|---|
| 办公 5 | 创业公司办公室 · 企业总部办公层 · 高管办公套间 · 联合办公空间 · 车库创业工位 |
| 餐饮 7 | 精品咖啡馆 · 工业风咖啡馆 · 日式居酒屋 · 中式茶楼 · 快餐店 · 米其林餐厅 · 面包烘焙坊 |
| 酒吧夜店 5 | 鸡尾酒吧 · 地下夜店 · 爵士酒吧 · 天台酒吧 · KTV 量贩包厢 |
| 教育 5 | 小学教学楼 · 大学阶梯教室楼 · 公共图书馆 · 化学实验室 · 幼儿园 |
| 居家 7 | 现代公寓 · 日式住宅 · 北欧住宅 · 复式 Loft · 老宅厢房 · 儿童房套间 · 别墅客厅 |
| 零售 5 | 便利店 · 时装精品店 · 独立书店 · 超市卖场 · 商场中庭 |
| 医疗健康 3 | 社区诊所 · 健身房 · 瑜伽 SPA 馆 |
| 文化娱乐 5 | 电影院 · 美术馆 · 博物馆 · 音乐排练室 · 电竞馆 |
| 交通住宿 4 | 酒店客房层 · 酒店大堂 · 机场候机厅 · 地铁站厅 |
| 工业特殊 4 | 数据中心 · 实验工厂 · 太空站舱室 · 赛博朋克黑客巢 |
每个场景自带:房间配比、层数、层高、平面尺寸、走廊型式、外墙做法、调色板、光照氛围、是否挑空。
桌台 13 · 座椅 16 · 收纳柜体 13 · 睡卧 7 · 厨吧 11 · 办公科技 14 · 灯具 15 · 软装摆设 50 · 专用大件 23。
每件元素声明四组信息,摆放引擎只认这些语义、不认家具名字(所以以后加新家具不用改算法):
w×d×h(米)——碰撞只看这三个数floor 地面自由 / wall 必须贴墙 / ceil 吊顶 / mount 挂墙(带挂高)/ seat 座位 / on 台面小件 / big 大件优先占位support 上面能再摆东西 · light 自身是光源(0–1.4 强度)· flat 贴地薄件不参与碰撞clutter ——摆在它台面上的小件候选(办公桌 → 显示器/笔电/马克杯/文件/台灯/笔筒)页面里的「🪑 元素图鉴」会把 161 件用当前场景调色板现场真渲染逐个拍出来看(不是贴图)。
① 平面怎么长——递归二分 BSP
@program 权重先展开成一串"房间实例"(大房型自动拆成好几间、每种带每层上限),
再按面积权重递归二分。每一刀都检查切完的两块会不会窄于 2.2 米——太窄换方向切,还窄就不切。
所以卫生间不会跟大厅一样大,也不会切出走不进去的缝。
② 门窗怎么开——先保证走得通
相邻房间共享墙边 ≥1.3 米算一条候选通路,先用 BFS 生成树保证每间房都走得到(从走廊出发),
再按概率额外开洞口让空间通透。门洞窗洞是从墙里扣出来的(下留窗台、上留过梁),不是贴门贴图。
③ 家具怎么摆——占用格 + 大件优先
每个房间是一张 0.25 米的占用格,摆一件先查空不空、摆完标脏,门口留 0.9 米通行带不许摆。
9 种摆法:grid 网格阵(可配对椅、可逐排抬高做阶梯座)/ cluster 一桌 N 椅 / wall 贴墙 /
row 沿最长墙一整条 / edge 沿刚摆那排外侧再排一溜(吧台外的吧凳)/ mount 挂墙 /
ceil 吊顶阵 / center / corner / scatter。
④ 最后一道:台面摆设
凡是 support 的家具(桌/柜/床/吧台),自动在上表面再撒 1–4 件小东西。
这一层是"有人住过"的关键——没有它,整个空间像样板间。
⑤ 一切随机由 (seed, 位置, 用途) 决定:同一份文本永远长出同一个空间,换 seed 就是另一套布局。
| 项目 | 命令/方式 | 结果 |
|---|---|---|
| 全场景生成回归 | 50 场景 × 5 seed = 250 次真跑 build() | ✅ 房间重叠 0、孤立走不到的房间 0、家具 33–1041 件 |
| 元素工厂 | 161 元素 × 3 变体调 make() | ✅ 483 次全通过,零件尺寸/材质全合法,无空元素 |
| 浏览器运行时 | Playwright + Chromium 真开页面、真点按钮 | ✅ 运行时错误 0 条 |
| 场景库 | 页面内计数 | ✅ 50 张卡片,点击即换场景 |
| 元素图鉴 | 逐个真渲染截图 | ✅ 161 张,桌椅柜床灯逐件可辨认 |
| 第一人称漫游 | 进漫游 + 按 W 前进 + 转头 | ✅ 能走、墙是硬的、楼梯能上、天花板自动装回 |
手画平面图 @plan | 现代公寓模板 | ✅ 6 间房 82 件家具,L 形房间不再重叠 |
| 多层 + 中庭 + 楼层切换 | 商场中庭 3 层 | ✅ 61 间房 589 件家具,楼层按钮 全部/3/2/1 层可切 |
| 线上部署 | wrangler pages deploy | ✅ HTTP 200 |
| 线上内容 = 仓库内容 | 4 个文件逐个 sha256 比对 | ✅ 4/4 字节完全一致 |
| 线上字节真渲染 | 把线上文件下载后用浏览器真跑 6 个差异最大的场景 | ✅ 全部正常出图,0 运行时错误 |
修掉的三个真 bug(都是先跑出来才发现的):
1. L 形手画房间用"包围盒"当房间 → 两间房重叠、墙叠墙 → 改成矩形分解 + 同组之间不砌墙
2. 走廊逐层随机 + 边角料被丢弃 → 楼层对不齐、房间之间留 0.4 米缝走不通 → 走廊整栋算一次、边角料降级成储藏间/回廊
3. 每块空地各展开一遍配比 → 一间咖啡馆长出 3 个后厨 4 个吧台 → 改成整层只展开一次 + 每类房型每层上限
诚实边界(没做到的):
1. 打开 https://metazero-terminal.pages.dev/roomtext/
2. 点 「🏛️ 场景库 50」 → 随便点一张卡片 → 右边立刻长出那个空间
3. 点 「🚶 走进去」 → WASD 走路、拖拽转头、Shift 加速、楼梯能上去(手机:左半屏摇杆走,右半屏转头)
4. 想改就改左边文本 → 点 「▶ 生成」;点 「🌱 换种子」 就是同一套规则下的另一套布局
5. 点 「🪑 元素图鉴」 看 161 件家具长什么样、多大、什么语义;点 「📖 语法」 看完整语法与系统设计
语法速查:
`
@meta scene=cafe_industrial size=20x15 levels=2 ceil=4.2 seed=42
palette=industrial mood=day corridor=spine ext=brick void=1
@program cafe_hall*4 cafe_bar*1.8 kitchen_pro*1 restroom*.6
@legend C cafe_hall K kitchen_pro # 给 @plan 用
@plan (ASCII 平面图,一个字符一格)
@fill cafe_hall += plant_big*2, poster?.6 # 给这类房间多摆点
@fill cafe_hall -= booth_seat # 或者拿掉某件
@palette wood=#c08a50 accent=#ff8a3d # 手动改某一格颜色
@stair type=u # straight / l / u / spiral
@void rect(6,4,5,5) # 挖中庭
@spawn person 14 on=cafe_hall # 撒人
`
1. 接口对接:maptext 的 legend 里给建筑加 interior=<scene id>,进门时用 seed+格坐标 生成室内
2. 门的位置对齐:室外那栋楼的入口朝向 → 室内一层的外门位置
3. LOD:城市视角只画外壳,走近/进门才 build 室内(现在一栋 600㎡ 只要 50–120ms、7–20k 三角形,够用)
4. 共用渲染器:两边都是"盒子+圆柱合并成一个几何体",可以直接合到 maptext 的场景图里
*证据等级:本报告所有数字均来自本次真实运行输出(250 次生成回归、483 次元素工厂调用、Playwright 截图、curl 实测、sha256 比对),非估算。*