@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
196 lines (193 loc) • 7.67 kB
JavaScript
import { b as clearSceneBGLCache, i as isRenderingContextRegistered, d as createEmptyUniformBuffer, e as ensureSceneLightState, g as getSceneBindGroupLayout, S as SCENE_UBO_BYTES, f as createMappedBuffer, B as BU } from './index-DMbDahsc.esm.js';
async function rebuildRegisteredScenes(engine) {
clearSceneBGLCache();
engine._pbrFallbackTex = void 0;
for (const surface of engine.surfaces) {
for (const ctx of surface._renderingContexts) {
if (ctx._kind !== "scene") {
continue;
}
const scene = ctx;
if (!isRenderingContextRegistered(surface, scene) || scene._z) {
continue;
}
await rebuildSceneGpu(engine, scene);
}
}
}
async function rebuildSceneGpu(engine, scene) {
if (scene._envTextures) {
const { rebuildSceneEnvironment } = await import('./environment-recovery-tYD5QxX7.esm.js');
await runRecoveryStep("rebuilding environment textures", () => rebuildSceneEnvironment(engine, scene));
}
await runRecoveryStep("rebuilding material textures", () => rebuildSceneTextures(engine, scene));
await runRecoveryStep("rebuilding meshes", () => _rebuildMeshes(engine, scene));
if (scene._z) {
return;
}
if (scene.shadowGenerators.length > 0 || scene.lights.some((light) => light.shadowGenerator)) {
const { rebuildSceneShadowGenerators } = await import('./shadow-recovery-wZDR5ghJ.esm.js');
await runRecoveryStep("rebuilding shadows", () => rebuildSceneShadowGenerators(engine, scene));
}
if (scene._z) {
return;
}
const rebuilds = scene._renderables.filter((r) => !!r._rebuild).map((r) => r._rebuild);
scene._renderables.length = scene._uniformUpdaters.length = 0;
scene._meshDisposables.clear();
scene._meshAuxDisposables.clear();
if (scene._lightGpuState) {
scene._lightGpuState = void 0;
}
for (const [build, meshes] of scene._groups) {
const result = await runRecoveryStep("rebuilding material groups", () => build(scene, meshes));
if (scene._z) {
return;
}
meshes.r = scene._runtimeBuilds?.base(build, result.rebuildSingle) ?? result.rebuildSingle;
meshes.o = result.renderables;
scene._renderables.push(...result.renderables);
if (result.updater) {
scene._uniformUpdaters.push(result.updater);
}
}
if (rebuilds.length > 0) {
scene._renderables.push(...await runRecoveryStep("rebuilding renderables", () => rebuildRenderables(rebuilds)));
}
scene._renderables.sort((a, b) => a.order - b.order);
scene._renderableVersion++;
resetFrameGraphTasks(engine, scene);
scene._frameGraph.build();
}
async function rebuildRenderables(rebuilds) {
const rebuilt = [];
for (const rebuild of rebuilds) {
rebuilt.push(await rebuild());
}
return rebuilt;
}
async function runRecoveryStep(description, action) {
try {
return await action();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Device-lost Scene recovery failed while ${description}: ${message}`, { cause: error });
}
}
function resetFrameGraphTasks(engine, scene) {
for (const task of scene._frameGraph._tasks) {
if (!("_sceneUBO" in task && "_sceneBG" in task && "_opaqueBindings" in task)) {
continue;
}
const rt = task;
rt._sceneUBO = createEmptyUniformBuffer(engine, SCENE_UBO_BYTES);
rt._lightsUBO = ensureSceneLightState(engine, scene)._buffer;
rt._sceneBG = engine._device.createBindGroup({
layout: getSceneBindGroupLayout(engine),
entries: [
{ binding: 0, resource: { buffer: rt._sceneUBO } },
{ binding: 1, resource: { buffer: rt._lightsUBO } }
]
});
rt._opaqueBindings.length = 0;
rt._directBindings.length = 0;
rt._transparentBindings.length = 0;
rt._ob.length = 0;
rt._lastVersion = -1;
rt._sceneUboCacheKey.length = 0;
}
}
async function _rebuildMeshes(engine, scene) {
let skeletonFactory = null;
let morphFactory = null;
for (const mesh of scene.meshes) {
if (mesh._cpuPositions && mesh._cpuNormals && mesh._cpuIndices) {
const recoverShared = mesh._gpu._recoverShared;
mesh._gpu = recoverShared ? recoverShared(engine, mesh, uploadRetainedMesh) : uploadRetainedMesh(engine, mesh);
}
if (mesh.skeleton) {
skeletonFactory ??= (await import('./create-skeleton-BKApXCJ-.esm.js')).createSkeleton;
const old = mesh.skeleton;
const rebuilt = skeletonFactory(engine, old.joints, old.weights, old.boneCount, old.boneMatrices, old.joints1, old.weights1);
Object.assign(old, rebuilt);
}
if (mesh.morphTargets) {
morphFactory ??= (await import('./create-morph-targets-BfcHXAXK.esm.js')).createMorphTargets;
const old = mesh.morphTargets;
const rebuilt = morphFactory(
engine,
old.targets.map((t) => ({ positions: t.positions, normals: t.normals })),
mesh._cpuPositions ? mesh._cpuPositions.length / 3 : 0,
Array.from(old.weights)
);
Object.assign(old, rebuilt);
}
}
}
function uploadRetainedMesh(engine, mesh) {
const positions = mesh._cpuPositions;
const normals = mesh._cpuNormals;
const uvs = mesh._cpuUvs;
const indices = mesh._cpuGpuIndices ?? mesh._cpuIndices;
const device = engine._device;
let uvBuffer;
if (uvs && uvs.length > 0) {
uvBuffer = createMappedBuffer(engine, uvs, BU.VERTEX);
} else {
uvBuffer = device.createBuffer({ size: positions.length / 3 * 8, usage: BU.VERTEX, mappedAtCreation: true });
uvBuffer.unmap();
}
return {
positionBuffer: createMappedBuffer(engine, positions, BU.VERTEX),
normalBuffer: createMappedBuffer(engine, normals, BU.VERTEX),
tangentBuffer: mesh._cpuTangents ? createMappedBuffer(engine, mesh._cpuTangents, BU.VERTEX) : null,
uvBuffer,
uv2Buffer: mesh._cpuUv2s ? createMappedBuffer(engine, mesh._cpuUv2s, BU.VERTEX) : null,
colorBuffer: mesh._cpuColors ? createMappedBuffer(engine, mesh._cpuColors, BU.VERTEX) : null,
hasUv: !!uvs && uvs.length > 0,
hasUv2: !!mesh._cpuUv2s && mesh._cpuUv2s.length > 0,
hasTangent: !!mesh._cpuTangents && mesh._cpuTangents.length > 0,
hasColor: !!mesh._cpuColors && mesh._cpuColors.length > 0,
indexBuffer: createMappedBuffer(engine, indices, BU.INDEX),
// Capacity-reserved meshes retain exact active CPU geometry. Recovery intentionally collapses the
// reservation; the next capacity update may grow it again without exposing padded arrays publicly.
indexCount: indices.length,
indexFormat: mesh._cpuIndexFormat ?? mesh._gpu.indexFormat
};
}
async function rebuildSceneTextures(engine, scene) {
const seen = /* @__PURE__ */ new Set();
const visited = /* @__PURE__ */ new WeakSet();
const textures = [];
const visit = (value) => {
if (!value || typeof value !== "object") {
return;
}
const obj = value;
if (obj.texture && obj.view && obj.sampler && typeof obj.width === "number" && typeof obj.height === "number") {
const tex = obj;
if (!seen.has(tex)) {
seen.add(tex);
textures.push(tex);
}
return;
}
if (visited.has(value)) {
return;
}
visited.add(value);
for (const child of Object.values(obj)) {
visit(child);
}
};
for (const mesh of scene.meshes) {
visit(mesh.material);
}
if (textures.length === 0) {
return;
}
const { rebuildTexture2D } = await import('./texture-recovery-DOZbz_TC.esm.js');
await Promise.all(textures.map((texture) => rebuildTexture2D(engine, texture)));
}
export { _rebuildMeshes, rebuildRegisteredScenes, rebuildRenderables };
//# sourceMappingURL=recovery-rebuild-OAOymy7J.esm.js.map