UNPKG

lume

Version:

Build next-level interactive web applications.

102 lines (80 loc) 3.33 kB
// TODO move into utils/three/ folder import {MeshPhongMaterial} from 'three/src/materials/MeshPhongMaterial.js' import {Color} from 'three/src/math/Color.js' import type {Object3D} from 'three/src/core/Object3D.js' import type {Material} from 'three/src/materials/Material.js' import type {RenderItem} from 'three/src/renderers/webgl/WebGLRenderLists.js' import type {Quaternion} from 'three/src/math/Quaternion.js' import type {Camera} from 'three/src/cameras/Camera.js' import type {OrthographicCamera} from 'three/src/cameras/OrthographicCamera.js' import type {PerspectiveCamera} from 'three/src/cameras/PerspectiveCamera.js' export type TColor = Color | string | number export function isRenderItem(obj: any): obj is RenderItem { return 'geometry' in obj && 'material' in obj } export function disposeMaterial(obj: Object3D) { if (!isRenderItem(obj)) return // because obj.material can be a material or array of materials const materials: Material[] = ([] as Material[]).concat(obj.material) for (const material of materials) { material.dispose() } } export function disposeObject(obj: Object3D, removeFromParent = true, destroyGeometry = true, destroyMaterial = true) { if (!obj) return if (isRenderItem(obj)) { if (obj.geometry && destroyGeometry) obj.geometry.dispose() if (destroyMaterial) disposeMaterial(obj) } removeFromParent && queueMicrotask(() => { // if we remove children in the same tick then we can't continue traversing, // so we defer to the next microtask obj.parent && obj.parent.remove(obj) }) } type DisposeOptions = Partial<{ removeFromParent: boolean destroyGeometry: boolean destroyMaterial: boolean }> export function disposeObjectTree(obj: Object3D, disposeOptions: DisposeOptions = {}) { obj.traverse(node => { disposeObject(node, disposeOptions.removeFromParent, disposeOptions.destroyGeometry, disposeOptions.destroyMaterial) }) } export function quaternionApproximateEquals(a: Quaternion, b: Quaternion, epsilon: number) { return ( Math.abs(a.x - b.x) < epsilon && Math.abs(a.y - b.y) < epsilon && Math.abs(a.z - b.z) < epsilon && Math.abs(a.w - b.w) < epsilon ) } export function applyMaterial(obj: Object3D, material: Material, dispose = true) { if (!isRenderItem(obj)) return if (dispose && obj.material) disposeMaterial(obj) obj.material = material } export function setRandomColorPhongMaterial(obj: Object3D, dispose?: boolean, traverse?: boolean) { const randomColor = (0xffffff / 3) * Math.random() + 0xffffff / 3 setColorPhongMaterial(obj, randomColor, dispose, traverse) } export function setColorPhongMaterial(obj: Object3D, color: TColor, dispose?: boolean, traverse = true) { const material = new MeshPhongMaterial() material.color = new Color(color) if (traverse) obj.traverse(node => applyMaterial(node, material, dispose)) else applyMaterial(obj, material, dispose) } export function isPerspectiveCamera(camera: Camera): camera is PerspectiveCamera { return !!(camera as any).isPerspectiveCamera } export function isOrthographicCamera(camera: Camera): camera is OrthographicCamera { return !!(camera as any).isOrthographicCamera } export interface Disposable { dispose: () => void } export function isDisposable(o: any): o is Disposable { return !!(typeof o === 'object' && o && 'dispose' in o) }