From 41b94ef0f9b5e7278372dba92015846bf69a706d Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 19 Oct 2026 22:37:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(wireframe):=20=E5=8E=BB=E9=87=8D=E9=94=AE?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=B8=96=E7=95=8C=E5=9D=90=E6=A0=87=E5=B9=B6?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=89=AB=E6=8F=8F=E8=8A=82=E6=B5=81=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A8=A1=E5=9E=8B=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../play/static/js/unpacked/hornet_bundle.js | 27 +++++-------------- 3dtank_bundle.js | 27 +++++-------------- tools/hornet_3d_wireframe.js | 27 +++++-------------- 3 files changed, 21 insertions(+), 60 deletions(-) diff --git a/3dtank.com/play/static/js/unpacked/hornet_bundle.js b/3dtank.com/play/static/js/unpacked/hornet_bundle.js index 8d77dce..3914ae9 100644 --- a/3dtank.com/play/static/js/unpacked/hornet_bundle.js +++ b/3dtank.com/play/static/js/unpacked/hornet_bundle.js @@ -2082,9 +2082,9 @@ } catch (e) {} } - // 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份 - // (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同, - // 两套都画会出现「复制出一个不动的身体,过一会才跟上」) + // 结果级去重:同一 mesh 资源 + 同一节点名,只保留一份 + // 注意:**不要把世界坐标写进键**——坦克移动时两套实例的平移会差一帧, + // 键随位置变化会导致「这帧去重、下帧不去重」→ 模型闪烁。 var TpIds = new WeakMap(); var TpNextId = 1; function DedupNodes(nodes) { @@ -2096,9 +2096,7 @@ var Tp = N.tp; var Id = TpIds.get(Tp); if (!Id) { Id = TpNextId++; TpIds.set(Tp, Id); } - var M = N.node && N.node.p35_1; - var Pos = M ? (Math.round(M[12]) + ',' + Math.round(M[13]) + ',' + Math.round(M[14])) : '?'; - var Key = Id + ':' + N.name + ':' + Pos; + var Key = Id + ':' + N.name; if (Keys.has(Key)) continue; Keys.add(Key); Out.push(N); @@ -2106,20 +2104,9 @@ return Out; } - // 扫描节流:仅在主路径完全采不到时使用,且同一辆坦克至少间隔 10 帧 - var ScanTick = 0; - var LastScanAt = new WeakMap(); - function ShouldScanNow(tank) { - ScanTick++; - var Last = LastScanAt.get(tank) || 0; - if (ScanTick - Last < 10) return false; - LastScanAt.set(tank, ScanTick); - return true; - } - // ===== 提取坦克的mesh数据 ===== // 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体") - // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底) + // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(兜底) function GetTankMeshData(tank, stats) { var nodes = []; var Seen = new WeakSet(); @@ -2150,8 +2137,8 @@ }); } catch(e) {} } - // ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿) - if (nodes.length === 0 && ShouldScanNow(tank)) { + // ③ 仍然为空才做组件字段扫描(不加节流:节流会导致「这帧有模型、下帧只有点」的闪烁) + if (nodes.length === 0) { try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {} } // ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉) diff --git a/3dtank_bundle.js b/3dtank_bundle.js index 8d77dce..3914ae9 100644 --- a/3dtank_bundle.js +++ b/3dtank_bundle.js @@ -2082,9 +2082,9 @@ } catch (e) {} } - // 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份 - // (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同, - // 两套都画会出现「复制出一个不动的身体,过一会才跟上」) + // 结果级去重:同一 mesh 资源 + 同一节点名,只保留一份 + // 注意:**不要把世界坐标写进键**——坦克移动时两套实例的平移会差一帧, + // 键随位置变化会导致「这帧去重、下帧不去重」→ 模型闪烁。 var TpIds = new WeakMap(); var TpNextId = 1; function DedupNodes(nodes) { @@ -2096,9 +2096,7 @@ var Tp = N.tp; var Id = TpIds.get(Tp); if (!Id) { Id = TpNextId++; TpIds.set(Tp, Id); } - var M = N.node && N.node.p35_1; - var Pos = M ? (Math.round(M[12]) + ',' + Math.round(M[13]) + ',' + Math.round(M[14])) : '?'; - var Key = Id + ':' + N.name + ':' + Pos; + var Key = Id + ':' + N.name; if (Keys.has(Key)) continue; Keys.add(Key); Out.push(N); @@ -2106,20 +2104,9 @@ return Out; } - // 扫描节流:仅在主路径完全采不到时使用,且同一辆坦克至少间隔 10 帧 - var ScanTick = 0; - var LastScanAt = new WeakMap(); - function ShouldScanNow(tank) { - ScanTick++; - var Last = LastScanAt.get(tank) || 0; - if (ScanTick - Last < 10) return false; - LastScanAt.set(tank, ScanTick); - return true; - } - // ===== 提取坦克的mesh数据 ===== // 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体") - // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底) + // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(兜底) function GetTankMeshData(tank, stats) { var nodes = []; var Seen = new WeakSet(); @@ -2150,8 +2137,8 @@ }); } catch(e) {} } - // ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿) - if (nodes.length === 0 && ShouldScanNow(tank)) { + // ③ 仍然为空才做组件字段扫描(不加节流:节流会导致「这帧有模型、下帧只有点」的闪烁) + if (nodes.length === 0) { try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {} } // ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉) diff --git a/tools/hornet_3d_wireframe.js b/tools/hornet_3d_wireframe.js index ae97ecd..cfc49be 100644 --- a/tools/hornet_3d_wireframe.js +++ b/tools/hornet_3d_wireframe.js @@ -557,9 +557,9 @@ } catch (e) {} } - // 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份 - // (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同, - // 两套都画会出现「复制出一个不动的身体,过一会才跟上」) + // 结果级去重:同一 mesh 资源 + 同一节点名,只保留一份 + // 注意:**不要把世界坐标写进键**——坦克移动时两套实例的平移会差一帧, + // 键随位置变化会导致「这帧去重、下帧不去重」→ 模型闪烁。 var TpIds = new WeakMap(); var TpNextId = 1; function DedupNodes(nodes) { @@ -571,9 +571,7 @@ var Tp = N.tp; var Id = TpIds.get(Tp); if (!Id) { Id = TpNextId++; TpIds.set(Tp, Id); } - var M = N.node && N.node.p35_1; - var Pos = M ? (Math.round(M[12]) + ',' + Math.round(M[13]) + ',' + Math.round(M[14])) : '?'; - var Key = Id + ':' + N.name + ':' + Pos; + var Key = Id + ':' + N.name; if (Keys.has(Key)) continue; Keys.add(Key); Out.push(N); @@ -581,20 +579,9 @@ return Out; } - // 扫描节流:仅在主路径完全采不到时使用,且同一辆坦克至少间隔 10 帧 - var ScanTick = 0; - var LastScanAt = new WeakMap(); - function ShouldScanNow(tank) { - ScanTick++; - var Last = LastScanAt.get(tank) || 0; - if (ScanTick - Last < 10) return false; - LastScanAt.set(tank, ScanTick); - return true; - } - // ===== 提取坦克的mesh数据 ===== // 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体") - // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底) + // ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(兜底) function GetTankMeshData(tank, stats) { var nodes = []; var Seen = new WeakSet(); @@ -625,8 +612,8 @@ }); } catch(e) {} } - // ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿) - if (nodes.length === 0 && ShouldScanNow(tank)) { + // ③ 仍然为空才做组件字段扫描(不加节流:节流会导致「这帧有模型、下帧只有点」的闪烁) + if (nodes.length === 0) { try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {} } // ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉)