Commit Graph

8 Commits

Author SHA1 Message Date
troytt 11e8a39e4e fix(render): 还原原版暗黑2建筑与城门屋顶过道遮挡消失机制 (#72)
- roofs.ts: 将默认最低透明度 DEFAULT_MIN_ROOF_ALPHA 设为 0.0,还原原版遮挡完全隐形机制
- roofs.ts: getRoofBounds 引入 roofHeight 等距高程投影补偿,将悬空屋顶包围盒延伸至地面投影视野
- roofs.ts: 将 DEFAULT_CLUSTER_CELL_DISTANCE 增至 4,并在 clusterRoofs 中自动插值补全城门与建筑群间隙走廊网格单元
- act-scene.ts & d2map.ts: 在 Drawable 和 IsoDraw 中接线传递 DT1 roofHeight 高程属性
- tests/roofs.test.ts: 补充高程下投、哈洛加斯西门跨过道聚类遮挡及拱梁/上墙全消失测试

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 02:12:37 +00:00
troytt 3951e70ef4 Merge branch 'fix/issue-20' 2026-09-17 09:09:01 +00:00
troytt 11e2e42731 perf(render): optimize GL state with immutable texStorage2D, uniform caching, and configurable discard (fixes #20) 2026-09-17 08:16:05 +00:00
troytt 57c13bdc26 feat(renderer/roofs): implement dynamic roof fade when player enters shelter (fixes #44) 2026-09-17 06:19:01 +00:00
troytt d4fb176031 perf(renderer): implement Raw 8-bit GPU Index Texture (gl.R8) + Shader Hardware Palette Lookup (#34) 2026-09-16 02:10:34 +00:00
troytt 4cc49a01aa perf(render): 视口剔除 + 单批次合图 + 零分配顶点写入 (refs #16)
主循环掉到 20 tps 的原因不是"缺引擎",而是三件具体的事,这个改动把它们一次做掉。

1. 视口剔除。此前整张地图无条件提交:把 act-scene 的绘制顺序在 365 张烘焙关卡上
   静态重放,平均每帧 2 848 个四边形、最差 14 400 个(act4 迪亚波罗),而真正落在
   1280x720 视口里的只有 7%~11%。现在关卡建好时把每条绘制列表的包围盒预先摊平成
   一块 Float32Array,每帧线性扫描做四次浮点比较。实测平均 2 848 -> 204 个/帧
   (7.2%),最差 14 400 -> 437 个。

   之所以是线性扫描而不是二分或分桶:绘制列表并不按 y 单调(地面 0/365 单调,最大
   回退 9 520 px),任何空间索引都得重新合并回画家顺序。14 400 次比较约 15~30 µs,
   占 16 ms 预算的 0.2%,换来的是顺序原样保留。墙和对象那条两路深度归并里,剔除只
   跳过"画"这一步,游标照常前进——否则后面所有遮挡关系都会错位。

2. 一次 draw call。此前批次一遇到图集页切换就得断开,同一帧里页号来回横跳,实测
   平均 361 次、最差 3 077 次 draw call。现在每个在用的图集页各占一个纹理单元,
   单元号随顶点属性进着色器,片元按 switch 选采样器,只有在用页数超过
   MAX_TEXTURE_IMAGE_UNITS 时才断批。

   这里没有采用 issue 里原先设想的 TEXTURE_2D_ARRAY:页高差异极大(2048x1999 到
   2048x48),纹理数组要求各层等大,实测朴素做法显存涨 1.73 倍(最差 2.82 倍,单关
   156.8 MB),装箱后仍要 1.29 倍。而全部 365 张关卡里单关最多只有 10 个页,加角色
   图集 11 个,稳稳低于 WebGL2 保证的 16 个单元——多单元合批同样是 1 次 draw call,
   显存零增长,资源也不用重烘焙。

3. 零分配顶点写入。quad() 原本每次建 4 个临时数组再解构,实测 2 849 个四边形写一帧
   要 244.9 µs;展开成标量后 106.0 µs(2.31 倍),每帧少建 11 396 个临时数组。同时
   顶点从 6 个/四边形降到 4 个并改用索引缓冲,上传前先 orphan 一次缓冲区避免与 GPU
   抢同一块内存。

顺带修掉几个让问题一直看不见、或者纯属每帧浪费的地方:

- state.drawCalls 原本在 act-scene 和 walk 里都硬编码成 1,这个假读数正是问题长期
  没被发现的原因;现在读渲染器自己的计数,HUD 一并显示图元数、剔除数和帧耗时。
- pages.filter(...).length 每帧建一个临时数组只为数个数,改成循环。
- HUD 文本每帧重建(十次字符串拼接 + 四次 toFixed),改成 4 Hz。
- NPC 名牌每帧 innerHTML='' 再 createElement,改成复用元素只改位置。
- onTick 里的 playerMoving 用 let 遮蔽了外层同名变量,导致 onRender 永远读到 false、
  角色走路动画从不播放。改成赋值。

护栏:新增 tests/cull.test.ts,除边界条件外,用全部 365 张关卡真实数据重放一遍,
断言"开剔除后画出的序列必须是不开剔除时那个序列的子序列"(顺序一致、只少不多),
并守住剔除率。verify-renderer-lifecycle 从 26 条加到 37 条,锁住"60 个四边形在 3 个
页之间来回切只花 1 次 draw call"、超出单元预算恰好多花 1 次、计数器在 begin() 归零、
以及着色器里采样器数组的形状。

Refs #16
2026-09-15 01:30:39 +00:00
troytt a80ee2cafc fix(render): 修复 SpriteRenderer 显存泄漏与补充 WebGL 上下文保护 (fixes #9)
- 优化 createProgram:Shader 链接后立即 detach 并 deleteShader,链接失败自动清理 Program
- 新增 deleteAtlas:支持释放指定 GPU 纹理显存,并在活跃批次解绑时安全 flush
- 新增 dispose 与 isDisposed:释放全部托管纹理、VAO、VBO、Program 与事件监听,支持幂等和 context-lost 防御
- 补充 WebGL context lost/restored 监听与自动恢复提示
- act-scene/map-scene/net-scene/walk 接入生命周期管理与 beforeunload 资源释放
- 新增 verify:renderer 单元生命周期验证套件 (26 项断言全部通过)
2026-09-14 07:54:51 +00:00
troytt 897b3735d6 初始提交:从零实现的暗黑破坏神 II WebGL2 引擎(TypeScript + Vite,零运行时依赖)
内容概览
- 归档层:MPQ v1 容器(hash/block 表、Storm 加密、分扇区编解码),含 PKWARE
  implode/explode 移植;支持 HTTP Range 只读取所需字节。
- 格式层:dc6 / ds1 / dt1 / pal / pl2 / cel / pcx / tbl / sprite,以及 dcc / cof / bitstream。
- 地图层:等距投影场景构建(地板/墙/屋顶分层、逐格瓦片变体加权随机、sub-tile 碰撞),
  资源包(索引色 PNG 图集 + scene.json)离线烘焙与逐像素差分校验。
- 角色层:DCC + COF 合成(8 方向),法师行走/站立替换占位棋子。
- 页面:acts.html 全屏画布 + 三级选择器(章节 / 场景 / 细分场景),资源包优先、
  可回退到直读归档(?live=1)。
- 验证脚本:verify:all / implode / acts / d2 / packs / dcc / deploy / listfile /
  object-lookup / alignment / tiles / generators 等,均带断言与退出码。

数据与许可
- 游戏数据(samples/)与第三方参考源码(reference/)不入库:体积大且无再分发许可,
  见 THIRD_PARTY_NOTICES.md。
- 生成关卡(70 迷宫 + 31 野外)尚未烘焙,现状与修复清单见 HANDOVER.md §4f。
2026-09-13 18:10:28 +08:00