96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import { type Camera, type Material, type Object3D } from "three";
|
|
|
|
export type RendererRenderCategory =
|
|
| "ao-world"
|
|
| "post-ao-transparent"
|
|
| "overlay";
|
|
|
|
export const AO_WORLD_RENDER_LAYER = 0;
|
|
export const POST_AO_TRANSPARENT_RENDER_LAYER = 28;
|
|
export const OVERLAY_RENDER_LAYER = 29;
|
|
|
|
export const AO_WORLD_RENDER_LAYER_MASK = 1 << AO_WORLD_RENDER_LAYER;
|
|
export const POST_AO_TRANSPARENT_RENDER_LAYER_MASK =
|
|
1 << POST_AO_TRANSPARENT_RENDER_LAYER;
|
|
export const OVERLAY_RENDER_LAYER_MASK = 1 << OVERLAY_RENDER_LAYER;
|
|
export const ALL_RENDER_LAYER_MASK =
|
|
AO_WORLD_RENDER_LAYER_MASK |
|
|
POST_AO_TRANSPARENT_RENDER_LAYER_MASK |
|
|
OVERLAY_RENDER_LAYER_MASK;
|
|
|
|
export function getRendererRenderCategoryLayer(
|
|
category: RendererRenderCategory
|
|
) {
|
|
switch (category) {
|
|
case "ao-world":
|
|
return AO_WORLD_RENDER_LAYER;
|
|
case "post-ao-transparent":
|
|
return POST_AO_TRANSPARENT_RENDER_LAYER;
|
|
case "overlay":
|
|
return OVERLAY_RENDER_LAYER;
|
|
}
|
|
}
|
|
|
|
export function applyRendererRenderCategory(
|
|
root: Object3D,
|
|
category: RendererRenderCategory
|
|
) {
|
|
const layer = getRendererRenderCategoryLayer(category);
|
|
|
|
root.traverse((object) => {
|
|
object.layers.set(layer);
|
|
});
|
|
}
|
|
|
|
export function enableObjectForAllRendererRenderCategories(root: Object3D) {
|
|
root.traverse((object) => {
|
|
object.layers.mask = ALL_RENDER_LAYER_MASK;
|
|
});
|
|
}
|
|
|
|
export function enableCameraRendererRenderCategories(camera: Camera) {
|
|
camera.layers.mask = ALL_RENDER_LAYER_MASK;
|
|
}
|
|
|
|
export function isMaterialEligibleForAmbientOcclusion(
|
|
material: Material | Material[]
|
|
) {
|
|
const materials = Array.isArray(material) ? material : [material];
|
|
|
|
return materials.every((candidate) => {
|
|
const renderMaterial = candidate as Material & {
|
|
colorWrite?: boolean;
|
|
opacity?: number;
|
|
transparent?: boolean;
|
|
visible?: boolean;
|
|
};
|
|
const opacity = renderMaterial.opacity ?? 1;
|
|
|
|
return (
|
|
renderMaterial.visible !== false &&
|
|
renderMaterial.colorWrite !== false &&
|
|
renderMaterial.transparent !== true &&
|
|
opacity >= 0.999
|
|
);
|
|
});
|
|
}
|
|
|
|
export function applyRendererRenderCategoryFromMaterial(root: Object3D) {
|
|
root.traverse((object) => {
|
|
const renderable = object as Object3D & {
|
|
material?: Material | Material[];
|
|
};
|
|
|
|
if (renderable.material === undefined) {
|
|
return;
|
|
}
|
|
|
|
applyRendererRenderCategory(
|
|
object,
|
|
isMaterialEligibleForAmbientOcclusion(renderable.material)
|
|
? "ao-world"
|
|
: "post-ao-transparent"
|
|
);
|
|
});
|
|
}
|