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 收掉)