fix(wireframe): 去掉炮塔门槛并加实体节点扫描兜底,修复只有点没有模型
This commit is contained in:
1 parent
dcf9a57456
commit
1dd7143e67
3 files changed
+262
-7
No files matched your search
@@ -1786,9 +1786,16 @@
|
||||
}
|
||||
|
||||
// 炮塔 BP 根节点
|
||||
// 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点
|
||||
function GetTurretNode(entity) {
|
||||
var wp = FindComponentByField(entity, 'a11p_1');
|
||||
if (wp) {
|
||||
if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1;
|
||||
if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} }
|
||||
}
|
||||
var comp = GetSkinComponent(entity);
|
||||
return (comp && comp.a17q_1) ? comp.a17q_1 : null;
|
||||
if (comp && comp.a17q_1) return comp.a17q_1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 兼容两种集合:引擎列表类(e1/g1)与迭代器(t/u/v)
|
||||
@@ -1866,6 +1873,7 @@
|
||||
var GpuCacheClearInterval = 1800; // 定期清 GPU 数据缓存(getBufferSubData 会停顿 GPU,太频繁会周期性卡顿)
|
||||
|
||||
var StrideDiagPrinted = new WeakSet();
|
||||
var GpuFailLogged = new WeakSet();
|
||||
function GetGpuData(tp) {
|
||||
if (!tp) return null;
|
||||
if (GpuDataCache.has(tp)) return GpuDataCache.get(tp);
|
||||
@@ -1907,6 +1915,33 @@
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// 采集失败诊断(每个资源只打一次):直接指出卡在顶点、索引还是 GL 上下文
|
||||
if (!GpuFailLogged.has(tp)) {
|
||||
GpuFailLogged.add(tp);
|
||||
var D = {};
|
||||
try {
|
||||
var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null;
|
||||
var B = G && G.h3x_1;
|
||||
var Dev = B && B.s3h_1;
|
||||
var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null;
|
||||
var IB = I && (I.q4w_1 || I.u3p_1);
|
||||
D = {
|
||||
顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1,
|
||||
顶点缓冲资源: !!B,
|
||||
缓冲包装: !!(B && B.v3x_1),
|
||||
原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1),
|
||||
设备: !!Dev,
|
||||
原生GL: !!(Dev && Dev.f75_1),
|
||||
顶点数: B ? B.w3x_1 : -1,
|
||||
位置长度: positions ? positions.length : -1,
|
||||
索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1,
|
||||
索引资源: !!IB,
|
||||
索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1),
|
||||
索引个数: indices ? indices.length : -1
|
||||
};
|
||||
} catch (e) {}
|
||||
console.warn('[透视][GPU] 采集失败', D);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1956,6 +1991,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底采集:完全不依赖皮肤组件结构,直接扫实体组件的字段值,找 3D 节点
|
||||
// 新版节点(zl 基类)特征:g35_1 = 名字字符串,p35_1 = 世界矩阵(长度≥16),r35_1 = 子节点列表
|
||||
function IsSceneNode(V) {
|
||||
return !!(V && typeof V === 'object' &&
|
||||
typeof V.g35_1 === 'string' &&
|
||||
V.p35_1 && typeof V.p35_1.length === 'number' && V.p35_1.length >= 16 &&
|
||||
V.r35_1 && typeof V.r35_1 === 'object');
|
||||
}
|
||||
|
||||
function ScanMeshNodes(tank, nodes, stats, Seen) {
|
||||
Seen = Seen || new WeakSet();
|
||||
function Try(V) {
|
||||
if (!IsSceneNode(V) || Seen.has(V)) return;
|
||||
Seen.add(V);
|
||||
CollectMeshNodes(V, nodes, 0, 'scan', stats);
|
||||
}
|
||||
try {
|
||||
tank.qz1_1.s2a_1.forEach(function (VL) {
|
||||
var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL;
|
||||
if (!comp) return;
|
||||
for (var K in comp) {
|
||||
var V = comp[K];
|
||||
if (!V || typeof V !== 'object') continue;
|
||||
if (IsSceneNode(V)) { Try(V); continue; }
|
||||
// 深度 2/3:节点常包在皮肤对象/包装对象里(如 skin.h159_1、weaponSkin.e11p_1.a17q_1)
|
||||
try {
|
||||
for (var K2 in V) {
|
||||
var V2 = V[K2];
|
||||
if (!V2 || typeof V2 !== 'object') continue;
|
||||
if (IsSceneNode(V2)) { Try(V2); continue; }
|
||||
for (var K3 in V2) {
|
||||
var V3 = V2[K3];
|
||||
if (V3 && typeof V3 === 'object') Try(V3);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ===== 提取坦克的mesh数据 =====
|
||||
function GetTankMeshData(tank, stats) {
|
||||
var nodes = [];
|
||||
@@ -1984,6 +2060,13 @@
|
||||
if (stats) stats.turretRoot = true;
|
||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null); } catch(e) {}
|
||||
}
|
||||
// 结构路径没拿到任何 mesh(皮肤/炮塔节点缺失或结构不符)时,直接扫实体所有组件字段找 3D 节点
|
||||
if (!skin || !turretNode || nodes.length === 0) {
|
||||
var Seen = new WeakSet();
|
||||
for (var ni = 0; ni < nodes.length; ni++) { if (nodes[ni] && nodes[ni].node) Seen.add(nodes[ni].node); }
|
||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||
if (stats) stats.hullSource = (stats.hullSource || '扫描');
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -2591,7 +2674,9 @@
|
||||
for (var gi = 0; gi < meshNodes.length; gi++) {
|
||||
if (meshNodes[gi].group === 'turret') turretCount++; else hullCount++;
|
||||
}
|
||||
if (turretCount === 0) { // 没有炮塔节点则不画模型,仅画兜底标记
|
||||
// 车身/炮塔任一有 mesh 就画模型;两者都为空才退回标记点
|
||||
// (旧逻辑要求必须先拿到炮塔节点,炮塔字段漂移时会连带把车身模型也吞掉)
|
||||
if (hullCount === 0 && turretCount === 0) {
|
||||
LogTankDiag(tankName, tankState, MeshStats, hullCount, turretCount, 0);
|
||||
DrawTankMarker(ctx, pos, camInfo, screenW, screenH, color);
|
||||
drawnCount++;
|
||||
|
||||
+87
-2
@@ -1786,9 +1786,16 @@
|
||||
}
|
||||
|
||||
// 炮塔 BP 根节点
|
||||
// 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点
|
||||
function GetTurretNode(entity) {
|
||||
var wp = FindComponentByField(entity, 'a11p_1');
|
||||
if (wp) {
|
||||
if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1;
|
||||
if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} }
|
||||
}
|
||||
var comp = GetSkinComponent(entity);
|
||||
return (comp && comp.a17q_1) ? comp.a17q_1 : null;
|
||||
if (comp && comp.a17q_1) return comp.a17q_1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 兼容两种集合:引擎列表类(e1/g1)与迭代器(t/u/v)
|
||||
@@ -1866,6 +1873,7 @@
|
||||
var GpuCacheClearInterval = 1800; // 定期清 GPU 数据缓存(getBufferSubData 会停顿 GPU,太频繁会周期性卡顿)
|
||||
|
||||
var StrideDiagPrinted = new WeakSet();
|
||||
var GpuFailLogged = new WeakSet();
|
||||
function GetGpuData(tp) {
|
||||
if (!tp) return null;
|
||||
if (GpuDataCache.has(tp)) return GpuDataCache.get(tp);
|
||||
@@ -1907,6 +1915,33 @@
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// 采集失败诊断(每个资源只打一次):直接指出卡在顶点、索引还是 GL 上下文
|
||||
if (!GpuFailLogged.has(tp)) {
|
||||
GpuFailLogged.add(tp);
|
||||
var D = {};
|
||||
try {
|
||||
var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null;
|
||||
var B = G && G.h3x_1;
|
||||
var Dev = B && B.s3h_1;
|
||||
var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null;
|
||||
var IB = I && (I.q4w_1 || I.u3p_1);
|
||||
D = {
|
||||
顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1,
|
||||
顶点缓冲资源: !!B,
|
||||
缓冲包装: !!(B && B.v3x_1),
|
||||
原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1),
|
||||
设备: !!Dev,
|
||||
原生GL: !!(Dev && Dev.f75_1),
|
||||
顶点数: B ? B.w3x_1 : -1,
|
||||
位置长度: positions ? positions.length : -1,
|
||||
索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1,
|
||||
索引资源: !!IB,
|
||||
索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1),
|
||||
索引个数: indices ? indices.length : -1
|
||||
};
|
||||
} catch (e) {}
|
||||
console.warn('[透视][GPU] 采集失败', D);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1956,6 +1991,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底采集:完全不依赖皮肤组件结构,直接扫实体组件的字段值,找 3D 节点
|
||||
// 新版节点(zl 基类)特征:g35_1 = 名字字符串,p35_1 = 世界矩阵(长度≥16),r35_1 = 子节点列表
|
||||
function IsSceneNode(V) {
|
||||
return !!(V && typeof V === 'object' &&
|
||||
typeof V.g35_1 === 'string' &&
|
||||
V.p35_1 && typeof V.p35_1.length === 'number' && V.p35_1.length >= 16 &&
|
||||
V.r35_1 && typeof V.r35_1 === 'object');
|
||||
}
|
||||
|
||||
function ScanMeshNodes(tank, nodes, stats, Seen) {
|
||||
Seen = Seen || new WeakSet();
|
||||
function Try(V) {
|
||||
if (!IsSceneNode(V) || Seen.has(V)) return;
|
||||
Seen.add(V);
|
||||
CollectMeshNodes(V, nodes, 0, 'scan', stats);
|
||||
}
|
||||
try {
|
||||
tank.qz1_1.s2a_1.forEach(function (VL) {
|
||||
var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL;
|
||||
if (!comp) return;
|
||||
for (var K in comp) {
|
||||
var V = comp[K];
|
||||
if (!V || typeof V !== 'object') continue;
|
||||
if (IsSceneNode(V)) { Try(V); continue; }
|
||||
// 深度 2/3:节点常包在皮肤对象/包装对象里(如 skin.h159_1、weaponSkin.e11p_1.a17q_1)
|
||||
try {
|
||||
for (var K2 in V) {
|
||||
var V2 = V[K2];
|
||||
if (!V2 || typeof V2 !== 'object') continue;
|
||||
if (IsSceneNode(V2)) { Try(V2); continue; }
|
||||
for (var K3 in V2) {
|
||||
var V3 = V2[K3];
|
||||
if (V3 && typeof V3 === 'object') Try(V3);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ===== 提取坦克的mesh数据 =====
|
||||
function GetTankMeshData(tank, stats) {
|
||||
var nodes = [];
|
||||
@@ -1984,6 +2060,13 @@
|
||||
if (stats) stats.turretRoot = true;
|
||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null); } catch(e) {}
|
||||
}
|
||||
// 结构路径没拿到任何 mesh(皮肤/炮塔节点缺失或结构不符)时,直接扫实体所有组件字段找 3D 节点
|
||||
if (!skin || !turretNode || nodes.length === 0) {
|
||||
var Seen = new WeakSet();
|
||||
for (var ni = 0; ni < nodes.length; ni++) { if (nodes[ni] && nodes[ni].node) Seen.add(nodes[ni].node); }
|
||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||
if (stats) stats.hullSource = (stats.hullSource || '扫描');
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -2591,7 +2674,9 @@
|
||||
for (var gi = 0; gi < meshNodes.length; gi++) {
|
||||
if (meshNodes[gi].group === 'turret') turretCount++; else hullCount++;
|
||||
}
|
||||
if (turretCount === 0) { // 没有炮塔节点则不画模型,仅画兜底标记
|
||||
// 车身/炮塔任一有 mesh 就画模型;两者都为空才退回标记点
|
||||
// (旧逻辑要求必须先拿到炮塔节点,炮塔字段漂移时会连带把车身模型也吞掉)
|
||||
if (hullCount === 0 && turretCount === 0) {
|
||||
LogTankDiag(tankName, tankState, MeshStats, hullCount, turretCount, 0);
|
||||
DrawTankMarker(ctx, pos, camInfo, screenW, screenH, color);
|
||||
drawnCount++;
|
||||
|
||||
@@ -261,9 +261,16 @@
|
||||
}
|
||||
|
||||
// 炮塔 BP 根节点
|
||||
// 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点
|
||||
function GetTurretNode(entity) {
|
||||
var wp = FindComponentByField(entity, 'a11p_1');
|
||||
if (wp) {
|
||||
if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1;
|
||||
if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} }
|
||||
}
|
||||
var comp = GetSkinComponent(entity);
|
||||
return (comp && comp.a17q_1) ? comp.a17q_1 : null;
|
||||
if (comp && comp.a17q_1) return comp.a17q_1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 兼容两种集合:引擎列表类(e1/g1)与迭代器(t/u/v)
|
||||
@@ -340,7 +347,8 @@
|
||||
var GpuCacheFrameCount = 0;
|
||||
var GpuCacheClearInterval = 1800; // 定期清 GPU 数据缓存(getBufferSubData 会停顿 GPU,太频繁会周期性卡顿)
|
||||
|
||||
var StrideDiagPrinted = new WeakSet();
|
||||
var StrideDiagPrinted = new WeakSet();
|
||||
var GpuFailLogged = new WeakSet();
|
||||
function GetGpuData(tp) {
|
||||
if (!tp) return null;
|
||||
if (GpuDataCache.has(tp)) return GpuDataCache.get(tp);
|
||||
@@ -382,6 +390,33 @@
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// 采集失败诊断(每个资源只打一次):直接指出卡在顶点、索引还是 GL 上下文
|
||||
if (!GpuFailLogged.has(tp)) {
|
||||
GpuFailLogged.add(tp);
|
||||
var D = {};
|
||||
try {
|
||||
var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null;
|
||||
var B = G && G.h3x_1;
|
||||
var Dev = B && B.s3h_1;
|
||||
var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null;
|
||||
var IB = I && (I.q4w_1 || I.u3p_1);
|
||||
D = {
|
||||
顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1,
|
||||
顶点缓冲资源: !!B,
|
||||
缓冲包装: !!(B && B.v3x_1),
|
||||
原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1),
|
||||
设备: !!Dev,
|
||||
原生GL: !!(Dev && Dev.f75_1),
|
||||
顶点数: B ? B.w3x_1 : -1,
|
||||
位置长度: positions ? positions.length : -1,
|
||||
索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1,
|
||||
索引资源: !!IB,
|
||||
索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1),
|
||||
索引个数: indices ? indices.length : -1
|
||||
};
|
||||
} catch (e) {}
|
||||
console.warn('[透视][GPU] 采集失败', D);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -431,6 +466,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底采集:完全不依赖皮肤组件结构,直接扫实体组件的字段值,找 3D 节点
|
||||
// 新版节点(zl 基类)特征:g35_1 = 名字字符串,p35_1 = 世界矩阵(长度≥16),r35_1 = 子节点列表
|
||||
function IsSceneNode(V) {
|
||||
return !!(V && typeof V === 'object' &&
|
||||
typeof V.g35_1 === 'string' &&
|
||||
V.p35_1 && typeof V.p35_1.length === 'number' && V.p35_1.length >= 16 &&
|
||||
V.r35_1 && typeof V.r35_1 === 'object');
|
||||
}
|
||||
|
||||
function ScanMeshNodes(tank, nodes, stats, Seen) {
|
||||
Seen = Seen || new WeakSet();
|
||||
function Try(V) {
|
||||
if (!IsSceneNode(V) || Seen.has(V)) return;
|
||||
Seen.add(V);
|
||||
CollectMeshNodes(V, nodes, 0, 'scan', stats);
|
||||
}
|
||||
try {
|
||||
tank.qz1_1.s2a_1.forEach(function (VL) {
|
||||
var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL;
|
||||
if (!comp) return;
|
||||
for (var K in comp) {
|
||||
var V = comp[K];
|
||||
if (!V || typeof V !== 'object') continue;
|
||||
if (IsSceneNode(V)) { Try(V); continue; }
|
||||
// 深度 2/3:节点常包在皮肤对象/包装对象里(如 skin.h159_1、weaponSkin.e11p_1.a17q_1)
|
||||
try {
|
||||
for (var K2 in V) {
|
||||
var V2 = V[K2];
|
||||
if (!V2 || typeof V2 !== 'object') continue;
|
||||
if (IsSceneNode(V2)) { Try(V2); continue; }
|
||||
for (var K3 in V2) {
|
||||
var V3 = V2[K3];
|
||||
if (V3 && typeof V3 === 'object') Try(V3);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ===== 提取坦克的mesh数据 =====
|
||||
function GetTankMeshData(tank, stats) {
|
||||
var nodes = [];
|
||||
@@ -459,6 +535,13 @@
|
||||
if (stats) stats.turretRoot = true;
|
||||
try { CollectMeshNodes(turretNode, nodes, 0, 'turret', stats ? stats.turret : null); } catch(e) {}
|
||||
}
|
||||
// 结构路径没拿到任何 mesh(皮肤/炮塔节点缺失或结构不符)时,直接扫实体所有组件字段找 3D 节点
|
||||
if (!skin || !turretNode || nodes.length === 0) {
|
||||
var Seen = new WeakSet();
|
||||
for (var ni = 0; ni < nodes.length; ni++) { if (nodes[ni] && nodes[ni].node) Seen.add(nodes[ni].node); }
|
||||
try { ScanMeshNodes(tank, nodes, stats ? stats.scan : null, Seen); } catch (e) {}
|
||||
if (stats) stats.hullSource = (stats.hullSource || '扫描');
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -1066,7 +1149,9 @@
|
||||
for (var gi = 0; gi < meshNodes.length; gi++) {
|
||||
if (meshNodes[gi].group === 'turret') turretCount++; else hullCount++;
|
||||
}
|
||||
if (turretCount === 0) { // 没有炮塔节点则不画模型,仅画兜底标记
|
||||
// 车身/炮塔任一有 mesh 就画模型;两者都为空才退回标记点
|
||||
// (旧逻辑要求必须先拿到炮塔节点,炮塔字段漂移时会连带把车身模型也吞掉)
|
||||
if (hullCount === 0 && turretCount === 0) {
|
||||
LogTankDiag(tankName, tankState, MeshStats, hullCount, turretCount, 0);
|
||||
DrawTankMarker(ctx, pos, camInfo, screenW, screenH, color);
|
||||
drawnCount++;
|
||||
|
||||
Reference in new issue
Block a user