Adopt Shapespark vegetation assets

This commit is contained in:
2026-08-03 17:10:17 +08:00
parent cd9cde0127
commit dbb5705d8e
135 changed files with 9401 additions and 737 deletions

View File

@@ -105,26 +105,44 @@ catalog.MATERIALS 声明「是什么」 纯 Python无 bpy
不管源文件把原点放在哪(`tree.py:301-303`
`assemble()` 返回种植数量,**0 表示模型缺失或 style 未知**,调用方据此回退到程序化
`tree.py:277-279`
树。
### 材质在本地重建,不沿用源文件
### Shapespark 是当前模型树样式
`tree.py:12-16`:两个 vendored 模型的材质都不能直接用。apple 的贴图 57% 是透明的
(那是叶片卡),没有 alpha-clip 设置的话整个树冠会渲染成一块。
当前唯一模型树 style 是 `shapespark`,资产来自
`assets/models/custom/shapespark_plants/tree-*/model.gltf`。该目录是从
Shapespark low-poly plants kit 拆分出的单植物 glTF/bin贴图共享在
`assets/models/custom/shapespark_plants/textures/`
**vendored 资产的材质一律重建**,不要 `append` 源文件的材质。
`tree.py` 加载 12 个 tree 变体,每个变体 import 一次、bake 成共享 mesh datablock
OSM 树实例只 link 这些 mesh。变体选择必须确定性当前用 stable instance index 经过
golden-turn 序列选变体:
### 已删除的第三种 style 有记录
```python
variant = variants[int(((index * GOLDEN_TURN) % 1.0) * len(variants))]
```
拆分 glTF 会让 Blender 生成 `branch-01.001``branch-01.002` 这类重复材质名;
`tree.py` 必须按 base name 去重材质槽,否则 `export_cesium.py` 会为同一张贴图重复
bake Cesium 材质。
### 已删除的模型树 style 有记录
`tree.py:18-25` 记着 `polyhaven` style 被删的原因LOD1 对象不是整棵树,是给几何节点
散布用的树枝和叶簇,直接种出来是一地树枝,连同 78MB 资产一起删了)。
`apple``fattree` style 已在 Shapespark 方案被用户接受后删除apple 需要强抠图、
法线和 Cesium 曝光补偿,远近观感不如 Shapespark 稳定fattree 视觉风格不匹配。
对应资产目录 `assets/models/speedtree/apple_low/``assets/models/lyrog/fattree/`
也已删除。
**这类"试过、不行、为什么"的记录要保留。** 删掉它,下一个人会重新引入同一个资产。
## 灌木边缘实例有性能预算
`generate_scene.py` 的 scrub 不是只有贴地面:`natural=scrub` 会先生成
`Scrub_<way_id>` 地面覆盖,再沿边界实例化 `assets/models/custom/bush/bush.glb`
`Scrub_<way_id>` 地面覆盖,再沿边界实例化
`assets/models/custom/shapespark_plants/bush-03/model.gltf`
`add_scrub_edge_bushes()`),并在大 scrub 面内部稀疏补树。
这些 bush 是共享 mesh 的多 node 实例。共享 mesh 能压 GLB 体积,但 Cesium 近景 Follow
@@ -137,15 +155,15 @@ SCRUB_BUSH_SPACING = 1.8
SCRUB_BUSH_LIMIT_PER_PATCH = 60
```
在 nantaizi 上约 `266` 个 bush nodesscrub 地面覆盖仍保留。当前默认资产仍是
`assets/models/custom/bush/bush.glb``assets/models/custom/bush_low/bush_low.glb`
曾作为低模实验验证过:几何从 `63,762` vertices / `67,536` indices 降到
`4,044` / `4,050`,但用户实测视觉效果不理想,**不要直接把它设为默认**
在 nantaizi 上约 `266` 个 bush nodesscrub 地面覆盖仍保留。旧高模 bush 和
rejected `bush_low` 资产已删除:前者太重,后者几何从
`63,762` vertices / `67,536` indices 降到
`4,044` / `4,050`,但用户实测视觉效果不理想,不能作为默认方向
不要只为了“更满”把间距调回 1m 以下;那是在把预览流畅度换成近景装饰密度。若确实
需要更密的灌木,先做一个视觉可接受的低模 bush或做显式 LOD / instancing 方案,再
跑 nantaizi 的 Blender/Cesium/preview 验证。注意:`bush_low` 的文件仍有 `5.79 MB`
因为 6 张纹理没有同步压缩;继续压 GLB 总体体积应考虑纹理尺寸/KTX2而不是只减面。
跑 nantaizi 的 Blender/Cesium/preview 验证。继续压 GLB 总体体积应考虑贴图尺寸/KTX2
而不是只减面。
---

View File

@@ -114,7 +114,7 @@ cp config/examples/template.json config/areas/my-area.json
| 字段 | 默认 | 说明 |
|---|---|---|
| `treeStyle` | `"natural"` | 合法值见 `generate_scene.py:144``TREE_STYLES``natural``procedural`,加上 `tree.py` 注册的模型 style |
| `treeStyle` | `"natural"` | 合法值见 `generate_scene.py``TREE_STYLES``natural``procedural``shapespark` |
| `officeOverrides` | `""` | 旧名 `office_overrides` 仍被接受 |
### `outputs`(逃生舱)

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,47 @@
# Test Shapespark tree variants
## Goal
Use the split Shapespark low-poly plant assets as tree, scrub bush, and lawn
tuft variants, then validate `nantaizi-lake-innovation-valley`.
The user liked the Shapespark `bush-03` scrub replacement and wants to evaluate
whether the same kit can replace the current tree asset. If multiple tree
variants are not too expensive, the scene should use varied trees while staying
deterministic for the same OSM input.
## Requirements
1. Prefer Shapespark tree assets from
`assets/models/custom/shapespark_plants/tree-*/model.gltf`.
2. Use deterministic variant selection based on stable tree instance ordering;
do not use unseeded random or time-dependent randomness.
3. Preserve existing tree placement count and route/road/building behavior.
4. Preserve the existing scrub bush `bush-03` experiment.
5. Replace the old Poly Haven `shrub_02` lawn tuft asset with suitable
Shapespark grass assets.
6. Evaluate the cost of loading multiple tree and lawn tuft variants:
- number of shared tree meshes introduced
- number of shared lawn tuft meshes introduced
- texture/image/material counts
- output GLB size
- total nodes
7. Rebuild only `nantaizi-lake-innovation-valley` stages
`blender,cesium,preview`.
## Acceptance Criteria
- [ ] Tree generation uses Shapespark tree variants deterministically.
- [ ] Lawn tufts use Shapespark `grass-*` variants instead of `shrub_02`.
- [ ] `python3 -m py_compile blender/generate_scene.py blender/osmassets/tree.py` passes.
- [ ] `python3 -m unittest blender/tests/test_pure.py` passes.
- [ ] nantaizi `blender,cesium,preview` rebuild passes.
- [ ] GLB digest reports tree mesh/material/image/node cost.
- [ ] User can open the local preview and compare visual result.
## Out Of Scope
- Runtime LOD.
- hanyang validation.
- Texture compression / KTX2.
- Committing the experiment before user approval.

View File

@@ -0,0 +1,99 @@
# Shapespark tree variant experiment report
## Current Trial
`nantaizi-lake-innovation-valley` now uses:
- scrub bush: `assets/models/custom/shapespark_plants/bush-03/model.gltf`
- tree style: `shapespark`
- Shapespark tree variants: 12 deterministic variants from
`assets/models/custom/shapespark_plants/tree-*/model.gltf`
- lawn tufts: 3 Shapespark grass variants from
`assets/models/custom/shapespark_plants/grass-*/model.gltf`
- Cesium export uses a gentler Shapespark foliage profile:
`gain=1.12`, `saturation=1.0`, `emission=0.06`.
Variant selection is deterministic: it uses the stable tree instance index and
the golden-turn sequence, not runtime random state.
## Validation
Commands run:
```bash
python3 -m py_compile blender/generate_scene.py blender/osmassets/tree.py blender/export_cesium.py
python3 -m unittest blender/tests/test_pure.py
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-shapespark-tree-glb-digest.json
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-shapespark-grass-glb-digest.json
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-shapespark-toned-glb-digest.json
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-shapespark-toned-rebuild.json
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-shapespark-cleanup.json
python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-03-test-shapespark-tree-variants
```
Results:
- `SCENE_DONE.tree_style`: `shapespark`
- Trees planted: `197`
- Grass tufts planted: `270`
- Scrub bush nodes: `266`
- GLB nodes: `830`
- GLB meshes: `113`
- GLB materials: `32`
- GLB images: `18`
- GLB accessors after final rebuild: `675` (accessor count is a known glTF
exporter fluctuation and is ignored by parity)
- GLB size after color tuning: `20.50 MB`
- Shapespark tree mesh datablocks: `12`
- Shapespark tree materials: `7`
- Shapespark tree images: `7`
- Shapespark tree shared geometry: `6,255` vertices / `18,696` indices
- Shapespark lawn tuft mesh datablocks: `3`
- Shapespark lawn tuft materials: `2`
- Shapespark lawn tuft images: `2`
- Shapespark lawn tuft shared geometry: `84` vertices / `90` indices
## Assessment
The multi-tree cost is acceptable for this scene. Tree node count stays the
same because the implementation links shared mesh datablocks per instance.
The extra cost is only 11 more shared tree mesh datablocks than a single tree
variant, plus 7 tree texture/material entries. The total GLB is still smaller
than the previous apple-tree build because the Shapespark textures and meshes
are lighter.
Replacing the former Poly Haven `shrub_02` lawn tuft with Shapespark
`grass-01/02/03` further reduces shared tuft geometry from a high-detail plant
asset to only 3 tiny grass-card variants. The lawn tuft instance count stays
the same, so the visual density is comparable while the shared geometry cost is
effectively negligible.
The color issue came from reusing the apple-tree export profile for every
alpha-cut plant. Apple needs strong albedo gain / saturation / emission because
its atlas is unusually dark. Shapespark foliage does not; using the apple
profile made it read yellow-green and glowing. The final trial keeps the
alpha-mask cleanup but uses much lower exposure and emission for Shapespark
material names (`branch-*`, `shrubbery-*`, `high-grass-*`, `hedge-*`,
`clover-*`, `flowers-*`).
Compared with the pre-Shapespark scrub-density baseline (`31.96 MB` GLB), this
trial is about `11.46 MB` smaller while keeping the same object count budget:
`830` nodes, `197` trees, `270` lawn tufts, and `266` scrub bush instances.
The main savings come from replacing the original heavy scrub bush, the
high-detail lawn tuft asset, and the apple tree atlas with lighter shared
Shapespark assets.
## Cleanup
After user visual approval, the old vegetation assets and their runtime loaders
were removed:
- retired tree styles: `apple`, `fattree`
- removed assets: SpeedTree apple, Lyrog fattree, Poly Haven `shrub_02`,
original `custom/bush`, rejected `custom/bush_low`
- remaining model tree style: `shapespark`
- remaining external plant asset root:
`assets/models/custom/shapespark_plants/`
- nantaizi rebuild after cleanup stayed at `20.50 MB`, `830` nodes, `113`
meshes, `32` materials, `18` images

View File

@@ -0,0 +1,26 @@
{
"id": "test-shapespark-tree-variants",
"name": "test-shapespark-tree-variants",
"title": "Test Shapespark tree variants",
"description": "",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "dingkang",
"assignee": "dingkang",
"createdAt": "2026-08-03",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}