148 lines
4.1 KiB
JavaScript
148 lines
4.1 KiB
JavaScript
// Babylon.js 形态键适配器
|
|
class BabylonMorphTargetAdapter {
|
|
constructor() {
|
|
this.morphTargetCache = {};
|
|
}
|
|
|
|
buildCache(meshes) {
|
|
this.morphTargetCache = {};
|
|
this.meshes = meshes;
|
|
let totalTargets = 0;
|
|
|
|
meshes.forEach(mesh => {
|
|
const mtm = mesh.morphTargetManager;
|
|
if (!mtm) return;
|
|
|
|
|
|
for (let i = 0; i < mtm.numTargets; i++) {
|
|
const mt = mtm.getTarget(i);
|
|
if (!mt?.name) continue;
|
|
|
|
const lowerName = mt.name.toLowerCase();
|
|
|
|
if (!this.morphTargetCache[lowerName]) {
|
|
this.morphTargetCache[lowerName] = [];
|
|
}
|
|
this.morphTargetCache[lowerName].push(mt);
|
|
totalTargets++;
|
|
}
|
|
});
|
|
|
|
return totalTargets;
|
|
}
|
|
|
|
async warmupInvisible(scene) {
|
|
console.log('开始预热...');
|
|
const startTime = performance.now();
|
|
|
|
const allTargets = Object.values(this.morphTargetCache).flat();
|
|
const totalTargets = allTargets.length;
|
|
console.log(`预热 ${totalTargets} 个 morph targets`);
|
|
|
|
// 多轮预热,用不同值组合
|
|
const rounds = 10;
|
|
for (let r = 0; r < rounds; r++) {
|
|
const val = (r % 2 === 0) ? 1.0 : 0;
|
|
allTargets.forEach(mt => mt.influence = val);
|
|
scene.render();
|
|
await new Promise(r => requestAnimationFrame(r));
|
|
}
|
|
|
|
// 重置
|
|
allTargets.forEach(mt => mt.influence = 0);
|
|
scene.render();
|
|
|
|
// 等待几帧让 GPU 完全稳定
|
|
for (let i = 0; i < 5; i++) {
|
|
await new Promise(r => requestAnimationFrame(r));
|
|
}
|
|
|
|
console.log(`预热完成,耗时 ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
}
|
|
|
|
warmupShaders(scene) {
|
|
console.log('开始shader预热...');
|
|
const startTime = performance.now();
|
|
|
|
// 强制同步更新所有 morph target managers
|
|
this.meshes?.forEach(mesh => {
|
|
const mtm = mesh.morphTargetManager;
|
|
if (mtm) {
|
|
mtm.enableNormalMorphing = true;
|
|
mtm.enableTangentMorphing = true;
|
|
}
|
|
});
|
|
|
|
// 预热:强制触发着色器编译
|
|
// 使用多种值组合来触发所有可能的shader变体
|
|
const testValues = [0, 0.2, 0.4, 0.6, 0.8, 1.0];
|
|
|
|
for (let pass = 0; pass < testValues.length; pass++) {
|
|
const value = testValues[pass];
|
|
for (const targets of Object.values(this.morphTargetCache)) {
|
|
targets.forEach(mt => {
|
|
mt.influence = value;
|
|
});
|
|
}
|
|
|
|
// 每次设置后都渲染,确保shader编译
|
|
if (scene) {
|
|
scene.render();
|
|
}
|
|
}
|
|
|
|
// 重置所有影响值
|
|
for (const targets of Object.values(this.morphTargetCache)) {
|
|
targets.forEach(mt => {
|
|
mt.influence = 0;
|
|
});
|
|
}
|
|
|
|
// 最后渲染一次确保重置生效
|
|
if (scene) {
|
|
scene.render();
|
|
}
|
|
|
|
const elapsed = performance.now() - startTime;
|
|
console.log(`shader预热完成,耗时 ${elapsed.toFixed(2)}ms`);
|
|
}
|
|
|
|
setInfluence(name, value) {
|
|
const lowerName = name.toLowerCase();
|
|
const targets = this.morphTargetCache[lowerName];
|
|
|
|
if (targets?.length) {
|
|
targets.forEach(mt => {
|
|
mt.influence = value;
|
|
});
|
|
}
|
|
}
|
|
|
|
getInfluence(name) {
|
|
const lowerName = name.toLowerCase();
|
|
const targets = this.morphTargetCache[lowerName];
|
|
|
|
if (targets?.length) {
|
|
return targets[0].influence;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
resetAll() {
|
|
for (const targets of Object.values(this.morphTargetCache)) {
|
|
targets.forEach(mt => {
|
|
mt.influence = 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
getCacheSize() {
|
|
return Object.keys(this.morphTargetCache).length;
|
|
}
|
|
}
|
|
|
|
// 导出到全局
|
|
if (typeof window !== 'undefined') {
|
|
window.BabylonMorphTargetAdapter = BabylonMorphTargetAdapter;
|
|
}
|