UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

164 lines 6.23 kB
import { Color } from 'three'; import { whiteTexture } from '../three'; // See https://repalash.com/blog/interactive-lerp-animation for details export function lerpVal(time, val, end) { if (typeof val === 'number' && typeof end === 'number') { return lerpNumber(time, val, end); } if (typeof val?.r === 'number' && typeof end?.r === 'number') { return new Color().set(lerpNumber(time.from ? { ...time, from: time.from.r } : time, val.r, end.r), lerpNumber(time.from ? { ...time, from: time.from.g } : time, val.g, end.g), lerpNumber(time.from ? { ...time, from: time.from.b } : time, val.b, end.b)); } if (val?.toArray && end?.toArray && val.clone && val.fromArray) { return lerpVector(time, val, end); } // not handling textures here specifically, to avoid texture arrays for now return time.t >= 1 ? end : val; } export function lerpNumber(time, val, end) { const { t, dt } = time; let newVal; if (t <= 0) newVal = time.from !== undefined ? time.from : val; else if (t >= 1.) newVal = end; else if (time.from !== undefined) { newVal = time.from * (1 - t) + end * t; } else { const l = 1 - t; const k = Math.max(0, Math.min(dt, l) / l); newVal = val + (end - val) * k; } return newVal; } export function lerpVector(time, val, end) { const valA = val.toArray(); const endA = end.toArray(); const fromA = time.from ? time.from.toArray() : undefined; const newValA = valA.map((v, i) => lerpVal(fromA ? { ...time, from: fromA[i] } : time, v, endA[i])); const newVal = val.clone().fromArray(newValA); return newVal; } export function lerpTexture(time, val, end) { if (!time.rm) { console.warn('MaterialManager: RenderManager is required for interpolating textures'); return end; } const { t, dt } = time; let newVal; const fromVal = time.from !== undefined ? time.from : val; if (t <= 0 || t > 0.99 || fromVal === end) { newVal = t <= 0 ? fromVal : end; let rt; if (val && val.userData?._lerpTexture && val.isRenderTargetTexture) { rt = val._target; } if (rt) { time.rm.releaseTempTarget(rt); } } else { const val1 = fromVal || whiteTexture; let needsInit = false; let rt; if (val && val.userData?._lerpTexture && val.isRenderTargetTexture) { rt = val._target; } else { const anyTex = fromVal || end || whiteTexture; const size = { width: anyTex.image?.width || anyTex.image?.naturalWidth || anyTex.image?.videoWidth || 1, height: anyTex.image?.height || anyTex.image?.naturalHeight || anyTex.image?.videoHeight || 1, }; rt = time.rm.getTempTarget({ size: size, colorSpace: anyTex.colorSpace, type: anyTex.type, format: anyTex.format, generateMipmaps: anyTex.generateMipmaps, minFilter: anyTex.minFilter, magFilter: anyTex.magFilter, wrapS: anyTex.wrapS, wrapT: anyTex.wrapT, }); rt.texture.userData._lerpTexture = true; needsInit = true; } if (rt) { if (time.from !== undefined || needsInit) { time.rm.blit(rt, { source: val1, respectColorSpace: false, transparent: true, clear: true, }); } const l = 1 - t; const k = Math.max(0, Math.min(dt, l) / l); // newVal = val + (end - val) * k // newVal = val * (1 - k) + end * k time.rm.blit(rt, { source: end || whiteTexture, respectColorSpace: false, transparent: true, clear: false, blendAlpha: time.from !== undefined ? t : k, // blending: NormalBlending, }); newVal = rt.texture; } else { newVal = val; } } return newVal; } export function lerpParams(params, obj, interpolateProps, time) { for (const key of Object.keys(params)) { if (!interpolateProps.has(key)) continue; const val = obj[key]; if (val === undefined) continue; const time2 = time.from ? { ...time, from: time.from[key] } : time; const end = params[key]; if (typeof val === 'number' && typeof end === 'number') { const newVal = lerpNumber(time2, val, end); if (newVal === val) delete params[key]; // no change else params[key] = newVal; } if (val?.isColor && end?.isColor) { const newVal = lerpVal(time2, val, end); if (newVal.getHex() === val.getHex()) delete params[key]; // no change else params[key] = newVal; } if ((val === undefined || val === null || val.isTexture) && (end === undefined || end === null || end.isTexture) && (val || end)) { const newVal = lerpTexture(time2, val || null, end || null); if (newVal === val) delete params[key]; // no change else params[key] = newVal; } // vectors and custom stuff if (val?.toArray && end?.toArray && val.clone && val.fromArray && val.equals) { const newVal = lerpVector(time2, val, end); if (newVal.equals(val)) delete params[key]; // no change else params[key] = newVal; } if (Array.isArray(val) && Array.isArray(end) && val.length === end.length) { // interpolate arrays const newVal = val.map((v, i) => lerpVal(time2, v, end[i])); if (newVal.every((v, i) => v === val[i])) delete params[key]; // no change else params[key] = newVal; } } } //# sourceMappingURL=lerp.js.map