@preference-sl/pref-viewer 2.16.0-beta.8 → 2.16.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/babylonjs-controller.js +145 -1
package/package.json
CHANGED
|
@@ -51,6 +51,36 @@ function normalizeDirection(x, y, z) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
const PREF_VIEWER_SIMPLIFIED_TRANSMISSION_METADATA_KEY = "prefViewerSimplifiedTransmission";
|
|
55
|
+
|
|
56
|
+
export function markPrefViewerSimplifiedTransmissionMaterial(mat) {
|
|
57
|
+
if (!mat) return mat;
|
|
58
|
+
mat.metadata = {
|
|
59
|
+
...(mat.metadata ?? {}),
|
|
60
|
+
[PREF_VIEWER_SIMPLIFIED_TRANSMISSION_METADATA_KEY]: true,
|
|
61
|
+
};
|
|
62
|
+
return mat;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isPrefViewerTransparentRenderOrderMaterial(mat) {
|
|
66
|
+
if (!mat) return false;
|
|
67
|
+
const isTransmissive = isPrefViewerPhysicalGlassMaterial(mat);
|
|
68
|
+
const isSimplifiedTransmission = mat.metadata?.[PREF_VIEWER_SIMPLIFIED_TRANSMISSION_METADATA_KEY] === true;
|
|
69
|
+
const alpha = typeof mat.alpha === "number" ? mat.alpha : 1;
|
|
70
|
+
const isAlphaBlended =
|
|
71
|
+
alpha < 1 ||
|
|
72
|
+
mat.transparencyMode === PBRMaterial.PBRMATERIAL_ALPHABLEND ||
|
|
73
|
+
mat.transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND ||
|
|
74
|
+
mat.needAlphaBlending?.() === true ||
|
|
75
|
+
mat.needAlphaTesting?.() === true;
|
|
76
|
+
|
|
77
|
+
return Boolean(isTransmissive || isSimplifiedTransmission || isAlphaBlended);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isPrefViewerPhysicalGlassMaterial(mat) {
|
|
81
|
+
return Boolean(mat?.subSurface?.isRefractionEnabled || mat?.subSurface?.isTranslucencyEnabled);
|
|
82
|
+
}
|
|
83
|
+
|
|
54
84
|
export function getLightingCycleProfile(timeOfDay = 0.5) {
|
|
55
85
|
const normalizedTime = clamp01(timeOfDay);
|
|
56
86
|
const timeHours = normalizedTime * 24;
|
|
@@ -1628,6 +1658,81 @@ export default class BabylonJSController {
|
|
|
1628
1658
|
});
|
|
1629
1659
|
}
|
|
1630
1660
|
|
|
1661
|
+
/**
|
|
1662
|
+
* Fixes cross-GLTF transparency ordering. Detects transparent/transmissive meshes
|
|
1663
|
+
* (alpha-blended, alpha-tested, simplified environment transmission, or
|
|
1664
|
+
* KHR_materials_transmission) across all loaded containers and moves them to
|
|
1665
|
+
* renderingGroupId=1 with depth-write disabled.
|
|
1666
|
+
* This ensures opaque scene geometry (e.g. lamp cups behind a model window) is
|
|
1667
|
+
* written to the depth buffer first (group 0) so the glass layer composites on top
|
|
1668
|
+
* without occluding it.
|
|
1669
|
+
* @private
|
|
1670
|
+
* @returns {void}
|
|
1671
|
+
*/
|
|
1672
|
+
#applyTransmittanceRenderOrder() {
|
|
1673
|
+
if (!this.#scene) return;
|
|
1674
|
+
|
|
1675
|
+
const envMeshIds = new Set(
|
|
1676
|
+
this.#containers.environment?.assetContainer?.meshes.map((m) => m.uniqueId) ?? []
|
|
1677
|
+
);
|
|
1678
|
+
const modelMeshIds = new Set(
|
|
1679
|
+
this.#containers.model?.assetContainer?.meshes.map((m) => m.uniqueId) ?? []
|
|
1680
|
+
);
|
|
1681
|
+
|
|
1682
|
+
// Map each material to the model meshes using it — needed to detect shared materials
|
|
1683
|
+
// before modifying an env mesh's material (must clone to avoid breaking model glass).
|
|
1684
|
+
const matToModelMeshes = new Map();
|
|
1685
|
+
this.#scene.meshes.forEach((mesh) => {
|
|
1686
|
+
if (!modelMeshIds.has(mesh.uniqueId) || !mesh.material) return;
|
|
1687
|
+
if (!matToModelMeshes.has(mesh.material)) matToModelMeshes.set(mesh.material, new Set());
|
|
1688
|
+
matToModelMeshes.get(mesh.material).add(mesh.uniqueId);
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
this.#scene.meshes.forEach((mesh) => {
|
|
1692
|
+
const mat = mesh.material;
|
|
1693
|
+
if (!mat) return;
|
|
1694
|
+
const isTransmissive = isPrefViewerPhysicalGlassMaterial(mat);
|
|
1695
|
+
const isTransparentLike = isPrefViewerTransparentRenderOrderMaterial(mat);
|
|
1696
|
+
if (!isTransparentLike) return;
|
|
1697
|
+
|
|
1698
|
+
if (envMeshIds.has(mesh.uniqueId)) {
|
|
1699
|
+
// Environment mesh carrying a transmissive material introduced by a material swap
|
|
1700
|
+
// (initial-load materials are pre-simplified in #assetContainer_simplifyTransmissiveMaterials).
|
|
1701
|
+
// Clone if shared with a model mesh so the model's physical glass is not affected.
|
|
1702
|
+
let targetMat = mat;
|
|
1703
|
+
if (matToModelMeshes.has(mat)) {
|
|
1704
|
+
targetMat = mat.clone(`${mat.name}_env_simplified`);
|
|
1705
|
+
mesh.material = targetMat;
|
|
1706
|
+
}
|
|
1707
|
+
if (isTransmissive) {
|
|
1708
|
+
this.#simplifyTransmissiveMaterial(targetMat);
|
|
1709
|
+
}
|
|
1710
|
+
mesh.renderingGroupId = 1;
|
|
1711
|
+
targetMat.disableDepthWrite = true;
|
|
1712
|
+
targetMat.alphaIndex = 0;
|
|
1713
|
+
} else if (modelMeshIds.has(mesh.uniqueId)) {
|
|
1714
|
+
// Model transparent materials render after all group-0 geometry.
|
|
1715
|
+
mesh.renderingGroupId = 1;
|
|
1716
|
+
mat.disableDepthWrite = true;
|
|
1717
|
+
|
|
1718
|
+
// Physical model glass keeps transmission, but strips volume absorption (tintColor)
|
|
1719
|
+
// and roughness because those blur/darken transmitted geometry. Do not apply this
|
|
1720
|
+
// tuning to plain alpha-blended model materials: render-order and physical glass
|
|
1721
|
+
// material tuning are separate concerns.
|
|
1722
|
+
if (isTransmissive && mat.subSurface) {
|
|
1723
|
+
mat.subSurface.tintColor = Color3.White();
|
|
1724
|
+
mat.roughness = 0;
|
|
1725
|
+
}
|
|
1726
|
+
mat.alphaIndex = 1;
|
|
1727
|
+
} else {
|
|
1728
|
+
// Unknown container (future-proof safe default).
|
|
1729
|
+
mesh.renderingGroupId = 1;
|
|
1730
|
+
mat.disableDepthWrite = true;
|
|
1731
|
+
mat.alphaIndex = 1;
|
|
1732
|
+
}
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1631
1736
|
/**
|
|
1632
1737
|
* Applies the showroom tonemapping curve and lighting bias used by the 3D viewer.
|
|
1633
1738
|
* @private
|
|
@@ -2522,6 +2627,40 @@ export default class BabylonJSController {
|
|
|
2522
2627
|
assetContainer.lights = [];
|
|
2523
2628
|
}
|
|
2524
2629
|
|
|
2630
|
+
/**
|
|
2631
|
+
* Strips KHR_materials_transmission, KHR_materials_ior and KHR_materials_volume from
|
|
2632
|
+
* environment-container (scene) materials, converting them to simple alpha blending.
|
|
2633
|
+
* Babylon.js cannot render two physical-transmission materials (from separate GLTF
|
|
2634
|
+
* containers) through each other: the scene decorative glass (lamp cups, etc.) is
|
|
2635
|
+
* simplified so the model's physical glass keeps full correct transmission.
|
|
2636
|
+
* @private
|
|
2637
|
+
* @param {AssetContainer} assetContainer
|
|
2638
|
+
* @returns {void}
|
|
2639
|
+
*/
|
|
2640
|
+
#assetContainer_simplifyTransmissiveMaterials(assetContainer) {
|
|
2641
|
+
assetContainer.materials?.forEach((mat) => {
|
|
2642
|
+
if (!(mat instanceof PBRMaterial)) return;
|
|
2643
|
+
if (!mat.subSurface?.isRefractionEnabled && !mat.subSurface?.isTranslucencyEnabled) return;
|
|
2644
|
+
this.#simplifyTransmissiveMaterial(mat);
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
/**
|
|
2649
|
+
* Converts a single PBRMaterial from physical KHR_materials_transmission to simple alpha
|
|
2650
|
+
* blending. Shared helper used by both the at-load simplification and the post-swap pass.
|
|
2651
|
+
* @private
|
|
2652
|
+
* @param {PBRMaterial} mat
|
|
2653
|
+
* @returns {void}
|
|
2654
|
+
*/
|
|
2655
|
+
#simplifyTransmissiveMaterial(mat) {
|
|
2656
|
+
const intensity = mat.subSurface.refractionIntensity ?? 1.0;
|
|
2657
|
+
mat.subSurface.isRefractionEnabled = false;
|
|
2658
|
+
mat.subSurface.isTranslucencyEnabled = false;
|
|
2659
|
+
mat.alpha = Math.max(0.28, 1.0 - intensity * 0.65);
|
|
2660
|
+
mat.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
|
2661
|
+
markPrefViewerSimplifiedTransmissionMaterial(mat);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2525
2664
|
/**
|
|
2526
2665
|
* Assigns unique ids to every imported camera so Babylon.js does not reuse stale SSAO effects between reloads.
|
|
2527
2666
|
* @private
|
|
@@ -2695,6 +2834,9 @@ export default class BabylonJSController {
|
|
|
2695
2834
|
if (container.state.name === "model" || container.state.name === "environment") {
|
|
2696
2835
|
this.#assetContainer_retagCameras(assetContainer);
|
|
2697
2836
|
}
|
|
2837
|
+
if (container.state.name === "environment") {
|
|
2838
|
+
this.#assetContainer_simplifyTransmissiveMaterials(assetContainer);
|
|
2839
|
+
}
|
|
2698
2840
|
this.#replaceContainer(container, assetContainer);
|
|
2699
2841
|
container.state.setSuccess(true);
|
|
2700
2842
|
} else {
|
|
@@ -2721,6 +2863,7 @@ export default class BabylonJSController {
|
|
|
2721
2863
|
this.#checkModelMetadata(oldModelMetadata, newModelMetadata);
|
|
2722
2864
|
this.#setMaxSimultaneousLights();
|
|
2723
2865
|
this.#enhanceTextureQuality();
|
|
2866
|
+
this.#applyTransmittanceRenderOrder();
|
|
2724
2867
|
this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#containers.model.assetContainer);
|
|
2725
2868
|
// Apply stored highlight settings so fresh page loads respect persisted state
|
|
2726
2869
|
this.#setHighlightConfig({
|
|
@@ -3203,6 +3346,7 @@ export default class BabylonJSController {
|
|
|
3203
3346
|
setMaterialOptions() {
|
|
3204
3347
|
this.#stopRender();
|
|
3205
3348
|
const materialsOptionsSetted = this.#setOptions_Materials();
|
|
3349
|
+
this.#applyTransmittanceRenderOrder();
|
|
3206
3350
|
this.#startRender();
|
|
3207
3351
|
return materialsOptionsSetted;
|
|
3208
3352
|
}
|
|
@@ -3384,4 +3528,4 @@ export default class BabylonJSController {
|
|
|
3384
3528
|
this.#requestRender({ frames: 2 });
|
|
3385
3529
|
}
|
|
3386
3530
|
}
|
|
3387
|
-
}
|
|
3531
|
+
}
|