fix(wireframe): 采集改为单路径优先并按 mesh+位置去重,修复重复身体与卡顿
This commit is contained in:
1 parent
af8cde08d2
commit
797f28c8cd
3 files changed
+138
-21
No files matched your search
@@ -2082,7 +2082,44 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份
|
||||||
|
// (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同,
|
||||||
|
// 两套都画会出现「复制出一个不动的身体,过一会才跟上」)
|
||||||
|
var TpIds = new WeakMap();
|
||||||
|
var TpNextId = 1;
|
||||||
|
function DedupNodes(nodes) {
|
||||||
|
if (nodes.length < 2) return nodes;
|
||||||
|
var Keys = new Set();
|
||||||
|
var Out = [];
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
var N = nodes[i];
|
||||||
|
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;
|
||||||
|
if (Keys.has(Key)) continue;
|
||||||
|
Keys.add(Key);
|
||||||
|
Out.push(N);
|
||||||
|
}
|
||||||
|
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数据 =====
|
// ===== 提取坦克的mesh数据 =====
|
||||||
|
// 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体")
|
||||||
|
// ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底)
|
||||||
function GetTankMeshData(tank, stats) {
|
function GetTankMeshData(tank, stats) {
|
||||||
var nodes = [];
|
var nodes = [];
|
||||||
var Seen = new WeakSet();
|
var Seen = new WeakSet();
|
||||||
@@ -2092,12 +2129,12 @@
|
|||||||
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
||||||
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
||||||
}
|
}
|
||||||
// ① 车身根节点:皮肤组件 h159_1 是「所有部件节点」的总根(比逐个部件更完整)
|
// ① 主路径:车身根节点(包含全部部件节点)
|
||||||
if (skinComp && skinComp.h159_1) {
|
if (skinComp && skinComp.h159_1) {
|
||||||
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
||||||
}
|
}
|
||||||
// ② 部件表:某些构建下根节点不完整,仍逐个部件补一遍(Seen 自动去重)
|
// ② 根节点采不到才退回部件表
|
||||||
if (skin) {
|
if (nodes.length === 0 && skin) {
|
||||||
try {
|
try {
|
||||||
ForEachInList(skin.g17r_1, function (part) {
|
ForEachInList(skin.g17r_1, function (part) {
|
||||||
if (!part) return;
|
if (!part) return;
|
||||||
@@ -2113,16 +2150,18 @@
|
|||||||
});
|
});
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
// ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿)
|
||||||
|
if (nodes.length === 0 && ShouldScanNow(tank)) {
|
||||||
|
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||||
|
}
|
||||||
|
// ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉)
|
||||||
var turretNode = GetTurretNode(tank);
|
var turretNode = GetTurretNode(tank);
|
||||||
if (stats) stats.turretComp = !!turretNode;
|
if (stats) stats.turretComp = !!turretNode;
|
||||||
if (turretNode) {
|
if (turretNode) {
|
||||||
if (stats) stats.turretRoot = true;
|
if (stats) stats.turretRoot = true;
|
||||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
||||||
}
|
}
|
||||||
// ③ 无条件结构扫描兜底:把组件字段里能看到的 3D 节点全部补进来
|
return DedupNodes(nodes);
|
||||||
// (解决「有的坦克只有点」「有的只有头没有身子」——结构字段不完整时漏采)
|
|
||||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
|
||||||
return nodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
||||||
|
|||||||
+46
-7
@@ -2082,7 +2082,44 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份
|
||||||
|
// (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同,
|
||||||
|
// 两套都画会出现「复制出一个不动的身体,过一会才跟上」)
|
||||||
|
var TpIds = new WeakMap();
|
||||||
|
var TpNextId = 1;
|
||||||
|
function DedupNodes(nodes) {
|
||||||
|
if (nodes.length < 2) return nodes;
|
||||||
|
var Keys = new Set();
|
||||||
|
var Out = [];
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
var N = nodes[i];
|
||||||
|
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;
|
||||||
|
if (Keys.has(Key)) continue;
|
||||||
|
Keys.add(Key);
|
||||||
|
Out.push(N);
|
||||||
|
}
|
||||||
|
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数据 =====
|
// ===== 提取坦克的mesh数据 =====
|
||||||
|
// 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体")
|
||||||
|
// ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底)
|
||||||
function GetTankMeshData(tank, stats) {
|
function GetTankMeshData(tank, stats) {
|
||||||
var nodes = [];
|
var nodes = [];
|
||||||
var Seen = new WeakSet();
|
var Seen = new WeakSet();
|
||||||
@@ -2092,12 +2129,12 @@
|
|||||||
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
||||||
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
||||||
}
|
}
|
||||||
// ① 车身根节点:皮肤组件 h159_1 是「所有部件节点」的总根(比逐个部件更完整)
|
// ① 主路径:车身根节点(包含全部部件节点)
|
||||||
if (skinComp && skinComp.h159_1) {
|
if (skinComp && skinComp.h159_1) {
|
||||||
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
||||||
}
|
}
|
||||||
// ② 部件表:某些构建下根节点不完整,仍逐个部件补一遍(Seen 自动去重)
|
// ② 根节点采不到才退回部件表
|
||||||
if (skin) {
|
if (nodes.length === 0 && skin) {
|
||||||
try {
|
try {
|
||||||
ForEachInList(skin.g17r_1, function (part) {
|
ForEachInList(skin.g17r_1, function (part) {
|
||||||
if (!part) return;
|
if (!part) return;
|
||||||
@@ -2113,16 +2150,18 @@
|
|||||||
});
|
});
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
// ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿)
|
||||||
|
if (nodes.length === 0 && ShouldScanNow(tank)) {
|
||||||
|
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||||
|
}
|
||||||
|
// ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉)
|
||||||
var turretNode = GetTurretNode(tank);
|
var turretNode = GetTurretNode(tank);
|
||||||
if (stats) stats.turretComp = !!turretNode;
|
if (stats) stats.turretComp = !!turretNode;
|
||||||
if (turretNode) {
|
if (turretNode) {
|
||||||
if (stats) stats.turretRoot = true;
|
if (stats) stats.turretRoot = true;
|
||||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
||||||
}
|
}
|
||||||
// ③ 无条件结构扫描兜底:把组件字段里能看到的 3D 节点全部补进来
|
return DedupNodes(nodes);
|
||||||
// (解决「有的坦克只有点」「有的只有头没有身子」——结构字段不完整时漏采)
|
|
||||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
|
||||||
return nodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
||||||
|
|||||||
@@ -557,7 +557,44 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 结果级去重:同一 mesh 资源 + 同一世界平移,只保留一份
|
||||||
|
// (引擎里同一逻辑节点可能存在「皮肤树 / 场景树」两套实例,AABB/矩阵更新时机不同,
|
||||||
|
// 两套都画会出现「复制出一个不动的身体,过一会才跟上」)
|
||||||
|
var TpIds = new WeakMap();
|
||||||
|
var TpNextId = 1;
|
||||||
|
function DedupNodes(nodes) {
|
||||||
|
if (nodes.length < 2) return nodes;
|
||||||
|
var Keys = new Set();
|
||||||
|
var Out = [];
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
var N = nodes[i];
|
||||||
|
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;
|
||||||
|
if (Keys.has(Key)) continue;
|
||||||
|
Keys.add(Key);
|
||||||
|
Out.push(N);
|
||||||
|
}
|
||||||
|
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数据 =====
|
// ===== 提取坦克的mesh数据 =====
|
||||||
|
// 采集策略:**单一路径优先**,绝不把两条路径的结果混在一起(混在一起就是"复制身体")
|
||||||
|
// ① 皮肤组件 h159_1(实际渲染树根)→ ② 部件表 → ③ 组件字段扫描(节流兜底)
|
||||||
function GetTankMeshData(tank, stats) {
|
function GetTankMeshData(tank, stats) {
|
||||||
var nodes = [];
|
var nodes = [];
|
||||||
var Seen = new WeakSet();
|
var Seen = new WeakSet();
|
||||||
@@ -567,12 +604,12 @@
|
|||||||
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
stats.hullSource = skin ? 'g159_1' : (skinComp ? '有组件但无皮肤' : '无皮肤组件');
|
||||||
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
stats.hullParts = (skin && typeof skin.g17r_1.e1 === 'function') ? skin.g17r_1.e1() : -1;
|
||||||
}
|
}
|
||||||
// ① 车身根节点:皮肤组件 h159_1 是「所有部件节点」的总根(比逐个部件更完整)
|
// ① 主路径:车身根节点(包含全部部件节点)
|
||||||
if (skinComp && skinComp.h159_1) {
|
if (skinComp && skinComp.h159_1) {
|
||||||
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
try { CollectMeshNodes(skinComp.h159_1, nodes, 0, 'hull', stats ? stats.hull : null, Seen); } catch (e) {}
|
||||||
}
|
}
|
||||||
// ② 部件表:某些构建下根节点不完整,仍逐个部件补一遍(Seen 自动去重)
|
// ② 根节点采不到才退回部件表
|
||||||
if (skin) {
|
if (nodes.length === 0 && skin) {
|
||||||
try {
|
try {
|
||||||
ForEachInList(skin.g17r_1, function (part) {
|
ForEachInList(skin.g17r_1, function (part) {
|
||||||
if (!part) return;
|
if (!part) return;
|
||||||
@@ -588,16 +625,18 @@
|
|||||||
});
|
});
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
// ③ 仍然为空才做组件字段扫描(节流,避免每帧全量遍历造成卡顿)
|
||||||
|
if (nodes.length === 0 && ShouldScanNow(tank)) {
|
||||||
|
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||||
|
}
|
||||||
|
// ④ 炮塔(独立于车身根;重复实例由 DedupNodes 收掉)
|
||||||
var turretNode = GetTurretNode(tank);
|
var turretNode = GetTurretNode(tank);
|
||||||
if (stats) stats.turretComp = !!turretNode;
|
if (stats) stats.turretComp = !!turretNode;
|
||||||
if (turretNode) {
|
if (turretNode) {
|
||||||
if (stats) stats.turretRoot = true;
|
if (stats) stats.turretRoot = true;
|
||||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null, Seen); } catch(e) {}
|
||||||
}
|
}
|
||||||
// ③ 无条件结构扫描兜底:把组件字段里能看到的 3D 节点全部补进来
|
return DedupNodes(nodes);
|
||||||
// (解决「有的坦克只有点」「有的只有头没有身子」——结构字段不完整时漏采)
|
|
||||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
|
||||||
return nodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
// ===== 残留节点检测(世界矩阵"冻结")→ 拖拽跟随 =====
|
||||||
|
|||||||
Reference in new issue
Block a user