UNPKG

@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.

363 lines (357 loc) 11.9 kB
import { T as ThrowLiteError, c as computeAabb, F as F32 } from './index-By0tcgYN.esm.js'; let states = null; function getPreviousDeformableShadowBounds(generator, mesh, kind) { const providers = states?.get(generator)?.providers; let bounds = null; for (let i = providers?.length ?? 0, kindIndex = 0; --i > kindIndex; ) { const provider = providers[i]; if (provider?.applies(mesh)) { bounds = provider.getLocalBounds(mesh, bounds); } } return bounds; } function updateShadowMesh(entry) { const bounds = entry.provider.getLocalBounds(entry.source); const min = bounds?.[0] ?? entry.source.boundMin; const max = bounds?.[1] ?? entry.source.boundMax; const previous = entry._bounds; let changed = false; for (let i = 0; i < 3; i++) { changed = !Object.is(previous[i], min?.[i]) || !Object.is(previous[i + 3], max?.[i]) || changed; previous[i] = min?.[i]; previous[i + 3] = max?.[i]; } entry.shadow.boundMin = min; entry.shadow.boundMax = max; return changed; } function createShadowMesh(source, provider) { const shadow = Object.create(source); const entry = { source, shadow, provider, _bounds: [], _version: 0 }; Object.defineProperty(shadow, "worldMatrixVersion", { configurable: true, get: () => source.worldMatrixVersion + entry._version }); updateShadowMesh(entry); return entry; } function mapCasterMeshes(state, casterMeshes) { if (state.sourceMeshes === casterMeshes) { return state.shadowMeshes; } const entries = []; const shadowMeshes = casterMeshes.map((mesh) => { const provider = state.providers.find((candidate) => candidate?.applies(mesh)); if (!provider) { return mesh; } const entry = createShadowMesh(mesh, provider); entries.push(entry); return entry.shadow; }); state.sourceMeshes = casterMeshes; state.shadowMeshes = shadowMeshes; state.entries = entries; return shadowMeshes; } function prepareExistingState(generator, state) { const existing = generator._shadowTaskState; if (existing && existing._casterMeshes === state.sourceMeshes && state.shadowMeshes) { existing._casterMeshes = state.shadowMeshes; } } function restoreSourceCasters(taskState, casterMeshes) { taskState._casterMeshes = casterMeshes; } function enableDeformableShadowBounds(generator, provider) { const kindIndex = provider.kind === "morph" ? 1 : 0; let state = states?.get(generator); if (state) { if (!state.providers[kindIndex]) { state.providers[kindIndex] = provider; state.sourceMeshes = void 0; } return; } const preload = generator._preloadShadowTask; const ensure = generator._ensureShadowTaskState; const render = generator._renderShadowMap; if (!preload || !ensure || !render) { ThrowLiteError(328); } state = { providers: [], preload, ensure, render }; state.providers[kindIndex] = provider; (states ??= /* @__PURE__ */ new WeakMap()).set(generator, state); generator._preloadShadowTask = (casterMeshes) => preload(mapCasterMeshes(state, casterMeshes)); generator._ensureShadowTaskState = (engine, scene, casterMeshes) => { prepareExistingState(generator, state); const shadowMeshes = mapCasterMeshes(state, casterMeshes); const taskState = ensure(engine, scene, shadowMeshes); restoreSourceCasters(taskState, casterMeshes); return taskState; }; generator._renderShadowMap = (engine, taskState) => { const shadowMeshes = state.shadowMeshes; const sourceMeshes = state.sourceMeshes; if (!shadowMeshes || !sourceMeshes) { return render(engine, taskState); } for (const entry of state.entries ?? []) { if (updateShadowMesh(entry)) { entry._version++; } } taskState._casterMeshes = shadowMeshes; try { return render(engine, taskState); } finally { restoreSourceCasters(taskState, sourceMeshes); } }; } let caches$1 = null; function createCache(mesh, morphTargets) { const positions = mesh._cpuPositions; const base = positions?.length ? computeAabb(positions) : mesh.boundMin && mesh.boundMax ? [mesh.boundMin, mesh.boundMax] : null; return { positions, base, morphTargets, targets: morphTargets.targets, targetRanges: morphTargets.targets.map((target) => computeAabb(target.positions)), result: [ [0, 0, 0], [0, 0, 0] ] }; } function getCache$1(mesh, morphTargets) { const cache = (caches$1 ??= /* @__PURE__ */ new WeakMap()).get(mesh); if (cache && cache.positions === mesh._cpuPositions && cache.morphTargets === morphTargets && cache.targets === morphTargets.targets) { return cache; } const next = createCache(mesh, morphTargets); caches$1.set(mesh, next); return next; } const morphBoundsProvider = { kind: "morph", applies: (mesh) => !!mesh.morphTargets, getLocalBounds(mesh) { const morphTargets = mesh.morphTargets; if (!morphTargets) { return null; } const cache = getCache$1(mesh, morphTargets); if (!cache.base) { return null; } const min = cache.result[0]; const max = cache.result[1]; for (let axis = 0; axis < 3; axis++) { min[axis] = cache.base[0][axis]; max[axis] = cache.base[1][axis]; } for (let target = 0; target < morphTargets.count; target++) { const range = cache.targetRanges[target]; const weight = morphTargets.weights[target] ?? 0; if (!range || !weight) { continue; } const targetMin = weight < 0 ? range[1] : range[0]; const targetMax = weight < 0 ? range[0] : range[1]; for (let axis = 0; axis < 3; axis++) { min[axis] = min[axis] + targetMin[axis] * weight; max[axis] = max[axis] + targetMax[axis] * weight; } } return cache.result; } }; function enableMorphTargetShadows(generator) { enableDeformableShadowBounds(generator, morphBoundsProvider); } function setExtentCorners(corners, min, max) { for (let i = 0; i < 8; i++) { corners[i * 3] = i & 1 ? max[0] : min[0]; corners[i * 3 + 1] = i & 2 ? max[1] : min[1]; corners[i * 3 + 2] = i & 4 ? max[2] : min[2]; } } function extentCorners(min, max) { const c = new F32(24); setExtentCorners(c, min, max); return c; } function buildSkinnedBoneCorners(mesh) { const positions = mesh._cpuPositions; const skeleton = mesh.skeleton; if (!positions || positions.length === 0 || !skeleton || !skeleton.weights) { return null; } const vertexCount = positions.length / 3 | 0; const boneCount = skeleton.boneCount; const boneMin = new F32(boneCount * 3).fill(Number.POSITIVE_INFINITY); const boneMax = new F32(boneCount * 3).fill(Number.NEGATIVE_INFINITY); const boneUsed = new Uint8Array(boneCount); const accumulate = (joints, weights, vertex) => { const base = vertex * 4; for (let k = 0; k < 4; k++) { if (weights[base + k] > 0) { const bone = joints[base + k]; if (bone < boneCount) { const bo = bone * 3; const vo = vertex * 3; if (positions[vo] < boneMin[bo]) { boneMin[bo] = positions[vo]; } if (positions[vo + 1] < boneMin[bo + 1]) { boneMin[bo + 1] = positions[vo + 1]; } if (positions[vo + 2] < boneMin[bo + 2]) { boneMin[bo + 2] = positions[vo + 2]; } if (positions[vo] > boneMax[bo]) { boneMax[bo] = positions[vo]; } if (positions[vo + 1] > boneMax[bo + 1]) { boneMax[bo + 1] = positions[vo + 1]; } if (positions[vo + 2] > boneMax[bo + 2]) { boneMax[bo + 2] = positions[vo + 2]; } boneUsed[bone] = 1; } } } }; const joints0 = skeleton.joints; const weights0 = skeleton.weights; const joints1 = skeleton.joints1; const weights1 = skeleton.weights1; for (let v = 0; v < vertexCount; v++) { accumulate(joints0, weights0, v); if (joints1 && weights1) { accumulate(joints1, weights1, v); } } const bones = []; for (let b = 0; b < boneCount; b++) { if (boneUsed[b]) { const o = b * 3; bones.push({ boneIndex: b, corners: extentCorners([boneMin[o], boneMin[o + 1], boneMin[o + 2]], [boneMax[o], boneMax[o + 1], boneMax[o + 2]]) }); } } return bones; } function growCornersByMatrix(corners, matrix, min, max, offset = 0) { const m0 = matrix[offset], m1 = matrix[offset + 1], m2 = matrix[offset + 2], m4 = matrix[offset + 4], m5 = matrix[offset + 5], m6 = matrix[offset + 6], m8 = matrix[offset + 8], m9 = matrix[offset + 9], m10 = matrix[offset + 10], m12 = matrix[offset + 12], m13 = matrix[offset + 13], m14 = matrix[offset + 14]; for (let i = 0; i < 8; i++) { const lx = corners[i * 3]; const ly = corners[i * 3 + 1]; const lz = corners[i * 3 + 2]; const x = m0 * lx + m4 * ly + m8 * lz + m12; const y = m1 * lx + m5 * ly + m9 * lz + m13; const z = m2 * lx + m6 * ly + m10 * lz + m14; if (x < min[0]) { min[0] = x; } if (y < min[1]) { min[1] = y; } if (z < min[2]) { min[2] = z; } if (x > max[0]) { max[0] = x; } if (y > max[1]) { max[1] = y; } if (z > max[2]) { max[2] = z; } } } let caches = null; function getCache(mesh) { const skeleton = mesh.skeleton; if (!skeleton?.weights || !skeleton.boneMatrices) { return null; } const cache = (caches ??= /* @__PURE__ */ new WeakMap()).get(mesh); if (cache && cache.positions === mesh._cpuPositions && cache.skeleton === skeleton && cache.joints === skeleton.joints && cache.weights === skeleton.weights && cache.joints1 === skeleton.joints1 && cache.weights1 === skeleton.weights1) { return cache; } const boxes = buildSkinnedBoneCorners(mesh); const base = boxes ? [ [Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity] ] : null; for (const box of boxes ?? []) { for (let axis = 0; axis < 3; axis++) { base[0][axis] = Math.min(base[0][axis], box.corners[axis]); base[1][axis] = Math.max(base[1][axis], box.corners[21 + axis]); } } const next = { positions: mesh._cpuPositions, skeleton, joints: skeleton.joints, weights: skeleton.weights, joints1: skeleton.joints1, weights1: skeleton.weights1, boxes, base, composedCorners: new Float32Array(24), result: [ [0, 0, 0], [0, 0, 0] ] }; caches.set(mesh, next); return next; } function createSkeletonBoundsProvider(generator) { return { kind: "skeleton", applies: (mesh) => !!mesh.skeleton?.weights && !!mesh.skeleton.boneMatrices, getLocalBounds(mesh) { const bounds = getPreviousDeformableShadowBounds(generator, mesh); const cache = getCache(mesh); const boneMatrices = mesh.skeleton?.boneMatrices; if (!cache?.boxes || !boneMatrices) { return bounds; } const min = cache.result[0]; const max = cache.result[1]; if (bounds && cache.base) { for (let axis = 0; axis < 3; axis++) { min[axis] = Math.min(bounds[0][axis], cache.base[0][axis]); max[axis] = Math.max(bounds[1][axis], cache.base[1][axis]); } setExtentCorners(cache.composedCorners, min, max); } min[0] = min[1] = min[2] = Infinity; max[0] = max[1] = max[2] = -Infinity; for (const box of cache.boxes) { growCornersByMatrix(bounds ? cache.composedCorners : box.corners, boneMatrices, min, max, box.boneIndex * 16); } return Number.isFinite(min[0]) ? cache.result : bounds; } }; } function enableSkeletonShadows(generator) { enableDeformableShadowBounds(generator, createSkeletonBoundsProvider(generator)); } export { enableMorphTargetShadows, enableSkeletonShadows }; //# sourceMappingURL=viewerShadows-DxWYaMlb.esm.js.map