UNPKG

r3f-points-fx

Version:

React three fiber component for easily creating high performance particles meshes. Makes particles morphing from one arrangement to another a piece of cake.

434 lines (408 loc) 14.8 kB
import { jsxs as B, Fragment as tt, jsx as y } from "react/jsx-runtime"; import { useFBO as et } from "@react-three/drei"; import { useThree as rt, useFrame as it, createPortal as ot } from "@react-three/fiber"; import * as p from "react"; import * as a from "three"; import { Triangle as nt, Vector3 as st, Vector2 as E } from "three"; const at = (s) => { if (s < 0) return 0; const t = Math.ceil(Math.sqrt(s)); return t * t; }, d = new nt(), R = new st(), G = new E(), W = new E(), $ = new E(); class ut { constructor(t) { this.geometry = t.geometry, this.randomFunction = Math.random, this.indexAttribute = this.geometry.index, this.positionAttribute = this.geometry.getAttribute("position"), this.normalAttribute = this.geometry.getAttribute("normal"), this.colorAttribute = this.geometry.getAttribute("color"), this.uvAttribute = this.geometry.getAttribute("uv"), this.weightAttribute = null, this.distribution = null; } setWeightAttribute(t) { return this.weightAttribute = t ? this.geometry.getAttribute(t) : null, this; } build() { const t = this.indexAttribute, n = this.positionAttribute, e = this.weightAttribute, u = t ? t.count / 3 : n.count / 3, v = new Float32Array(u); for (let l = 0; l < u; l++) { let c = 1, f = 3 * l, A = 3 * l + 1, x = 3 * l + 2; t && (f = t.getX(f), A = t.getX(A), x = t.getX(x)), e && (c = e.getX(f) + e.getX(A) + e.getX(x)), d.a.fromBufferAttribute(n, f), d.b.fromBufferAttribute(n, A), d.c.fromBufferAttribute(n, x), c *= d.getArea(), v[l] = c; } const r = new Float32Array(u); let m = 0; for (let l = 0; l < u; l++) m += v[l], r[l] = m; return this.distribution = r, this; } setRandomGenerator(t) { return this.randomFunction = t, this; } sample(t, n, e, u) { const v = this.sampleFaceIndex(); return this.sampleFace(v, t, n, e, u); } sampleFaceIndex() { const t = this.distribution[this.distribution.length - 1]; return this.binarySearch(this.randomFunction() * t); } binarySearch(t) { const n = this.distribution; let e = 0, u = n.length - 1, v = -1; for (; e <= u; ) { const r = Math.ceil((e + u) / 2); if (r === 0 || n[r - 1] <= t && n[r] > t) { v = r; break; } else t < n[r] ? u = r - 1 : e = r + 1; } return v; } sampleFace(t, n, e, u, v) { let r = this.randomFunction(), m = this.randomFunction(); r + m > 1 && (r = 1 - r, m = 1 - m); const l = this.indexAttribute; let c = t * 3, f = t * 3 + 1, A = t * 3 + 2; return l && (c = l.getX(c), f = l.getX(f), A = l.getX(A)), d.a.fromBufferAttribute(this.positionAttribute, c), d.b.fromBufferAttribute(this.positionAttribute, f), d.c.fromBufferAttribute(this.positionAttribute, A), n.set(0, 0, 0).addScaledVector(d.a, r).addScaledVector(d.b, m).addScaledVector(d.c, 1 - (r + m)), e !== void 0 && (this.normalAttribute !== void 0 ? (d.a.fromBufferAttribute(this.normalAttribute, c), d.b.fromBufferAttribute(this.normalAttribute, f), d.c.fromBufferAttribute(this.normalAttribute, A), e.set(0, 0, 0).addScaledVector(d.a, r).addScaledVector(d.b, m).addScaledVector(d.c, 1 - (r + m)).normalize()) : d.getNormal(e)), u !== void 0 && this.colorAttribute !== void 0 && (d.a.fromBufferAttribute(this.colorAttribute, c), d.b.fromBufferAttribute(this.colorAttribute, f), d.c.fromBufferAttribute(this.colorAttribute, A), R.set(0, 0, 0).addScaledVector(d.a, r).addScaledVector(d.b, m).addScaledVector(d.c, 1 - (r + m)), u.r = R.x, u.g = R.y, u.b = R.z), v !== void 0 && this.uvAttribute !== void 0 && (G.fromBufferAttribute(this.uvAttribute, c), W.fromBufferAttribute(this.uvAttribute, f), $.fromBufferAttribute(this.uvAttribute, A), v.set(0, 0).addScaledVector(G, r).addScaledVector(W, m).addScaledVector($, 1 - (r + m))), this; } } const lt = (s, t) => { const n = s * 4, e = new Float32Array(n), u = new ut(t).build(), v = Math.sqrt(s), r = v; for (let l = 0; l < s; l++) { const c = l * 4, f = new a.Vector3(); u.sample(f), e[c] = f.x, e[c + 1] = f.y, e[c + 2] = f.z, e[c + 3] = 1; } const m = new a.DataTexture( e, v, r, a.RGBAFormat, a.FloatType ); return m.needsUpdate = !0, m; }, ct = (s, t) => { const n = t.geometry.attributes.position, e = new Float32Array(s * 4), u = Math.sqrt(s), v = u; let r = 0; for (let l = 0; l < s; l++) { const c = l * 4; if (r < n.count) { const f = r * 3; e[c] = n.array[f], e[c + 1] = n.array[f + 1], e[c + 2] = n.array[f + 2], e[c + 3] = 1, r++; } else { const f = Math.floor(Math.random() * n.count); e[c] = n.array[f * 3], e[c + 1] = n.array[f * 3 + 1], e[c + 2] = n.array[f * 3 + 2], e[c + 3] = 1; } } const m = new a.DataTexture( e, u, v, a.RGBAFormat, a.FloatType ); return m.needsUpdate = !0, m; }, ft = (s, t, n = !1) => n ? ct(s, t) : lt(s, t), k = (s) => Object.entries(s).reduce((e, [u, v]) => { const r = a.UniformsUtils.clone({ [u]: { value: v } }); return { ...e, ...r }; }, {}), mt = ` float progressModifier(vec3 origin, vec3 target, float progress){ return progress; } `, dt = (s) => ` uniform float uTime; uniform float uTransitionProgress; uniform sampler2D positionsA; uniform sampler2D positionsB; uniform int uModel1; uniform int uModel2; varying vec2 vUv; ${s || mt} void main() { vec3 model1 = texture2D(positionsA, vUv).rgb; vec3 model2 = texture2D(positionsB, vUv).rgb; float progress = progressModifier(model1, model2, uTransitionProgress); vec3 pos = mix(model1, model2, progress); gl_FragColor = vec4(pos, progress); } `, ht = ` varying vec2 vUv; varying float vTransitionProgress; void main() { vUv = uv; vec4 modelPosition = modelMatrix * vec4(position, 1.0); vec4 viewPosition = viewMatrix * modelPosition; vec4 projectedPosition = projectionMatrix * viewPosition; gl_Position = projectedPosition; } `, vt = ` vec4 modifier(int index){ // index is the current active model's index in model array passed // use gl_PointCoord and alpha to control the shape vec2 uv = gl_PointCoord; float distanceFromCenter = length(uv - 0.5); float alpha = ceil(max(0.5 - distanceFromCenter, 0.0)); vec3 color = uColor; vec4 result = vec4(color, uAlpha * alpha); return result; } `, pt = (s) => ` uniform vec3 uColor; uniform float uTime; uniform int uModel1; uniform int uModel2; uniform float uAlpha; varying vec3 vPosition; varying float vTransitionProgress; ${s || vt} void main() { vec4 color1 = modifier(uModel1); vec4 color2 = modifier(uModel2); gl_FragColor = mix(color1, color2, vTransitionProgress); } `, gt = ` VertexProperties modifier(vec3 pos, float progress){ VertexProperties result; result.position = pos; result.pointSize = uPointSize; result.progress = progress; return result; } `, bt = (s, t) => ` uniform sampler2D uPosition; uniform float uTime; uniform vec2 uViewPort; uniform float uDpr; uniform float uPointSize; uniform int uModel1; uniform int uModel2; varying vec3 vPosition; varying float vTransitionProgress; struct VertexProperties { vec3 position; float pointSize; float progress; }; ${s || gt} void main(){ vec4 textureData = texture2D(uPosition, position.xy); vec3 pos = textureData.xyz; float progress = textureData.w; VertexProperties res = modifier(pos, progress); vec4 modelPosition = modelMatrix * vec4(res.position, 1.0); vec4 viewPosition = viewMatrix * modelPosition; vec4 projectedPosition = projectionMatrix * viewPosition; gl_Position = projectedPosition; gl_PointSize = res.pointSize * uViewPort.y * uDpr; ${t ? "gl_PointSize *= (1.0 / abs(viewPosition.z));" : ""} vPosition = res.position; vTransitionProgress = res.progress; } `, xt = p.forwardRef( ({ models: s, pointsCount: t = 1e3, modelA: n = null, modelB: e = null, uniforms: u = {}, baseColor: v = new a.Color(0, 0, 0), pointSize: r = 0.1, alpha: m = 1, attributes: l = [], blending: c = a.AdditiveBlending, vertexModifier: f, fragmentModifier: A, progressModifier: x, sizeAttenutation: H = !0, organizedParticleIndexes: L = [], ...N }, J) => { const g = p.useRef(null), h = p.useRef(null), F = p.useRef(n), w = p.useRef(e), z = p.useRef(new a.Scene()), K = p.useRef( new a.OrthographicCamera(-1, 1, 1, -1, 1 / Math.pow(2, 53), 1) ), { gl: D, size: { width: V, height: _ } } = rt(), S = p.useMemo(() => at(t), [t]), P = p.useMemo(() => { const i = []; return s.forEach((o, b) => { i.push( ft( S, o, L.includes(b) ) ); }), i; }, [s, S]), Q = p.useMemo( () => k({ uTransitionProgress: 0, uTime: 0, positionsA: F.current !== null ? P[F.current] : null, positionsB: w.current !== null ? P[w.current] : null, uModel1: F.current, uModel2: w.current }), [P] ), Y = p.useMemo(() => k({ uPosition: null, uColor: v, uTime: 0, uModel1: F.current, uModel2: w.current, uPointSize: r / 10, uAlpha: m, uViewPort: new a.Vector2(V, _), uDpr: D.getPixelRatio(), ...u }), []), U = p.useCallback((i) => { var o; ((o = g.current) == null ? void 0 : o.material) instanceof a.ShaderMaterial && (g.current.material.uniforms.uTransitionProgress.value = Math.min( i, 1 )); }, []), j = p.useCallback( (i) => { var b; let o = null; i >= 0 && i < P.length && (o = i, F.current = o), ((b = g.current) == null ? void 0 : b.material) instanceof a.ShaderMaterial && (g.current.material.uniforms.positionsA.value = o !== null ? P[o] : null, g.current.material.uniforms.uModel1.value = o); }, [P] ), C = p.useCallback( (i) => { var b; let o = null; i >= 0 && i < P.length && (o = i, w.current = o), ((b = g.current) == null ? void 0 : b.material) instanceof a.ShaderMaterial && (g.current.material.uniforms.positionsB.value = o !== null ? P[o] : null, g.current.material.uniforms.uModel2.value = o); }, [P] ); p.useEffect(() => { var i; ((i = h.current) == null ? void 0 : i.material) instanceof a.ShaderMaterial && (h.current.material.uniforms.uColor.value = v, h.current.material.uniforms.uPointSize.value = r / 10, h.current.material.uniforms.uAlpha.value = m), Object.entries(u).forEach(([o, b]) => { var M; ((M = h.current) == null ? void 0 : M.material) instanceof a.ShaderMaterial && h.current.material.uniforms[o] && (h.current.material.uniforms[o].value = b); }); }, [v, r, m, u]), p.useEffect(() => { var i; ((i = h.current) == null ? void 0 : i.material) instanceof a.ShaderMaterial && (h.current.material.uniforms.uViewPort.value.set(V, _), h.current.material.uniforms.uDpr.value = D.getPixelRatio()); }, [V, _, D]); const O = p.useMemo(() => { const i = new Float32Array([ -1, -1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0 ]), o = new Float32Array([0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0]); return { positions: i, uvs: o }; }, []), X = et(Math.sqrt(S), Math.sqrt(S), { minFilter: a.NearestFilter, magFilter: a.NearestFilter, type: a.FloatType }), Z = p.useMemo(() => { const i = Math.sqrt(S), o = i, b = new Float32Array(S * 3); for (let M = 0; M < S; M++) { const T = M * 3; b[T + 0] = M % o / o, b[T + 1] = M / o / i; } return b; }, [S]); return p.useImperativeHandle( J, () => ({ getSimulationMesh: () => g.current, getPointsMesh: () => h.current, updateProgress: U, updateTime: (i) => { g.current && g.current.material instanceof a.ShaderMaterial && (g.current.material.uniforms.uTime.value = i), h.current && h.current.material instanceof a.ShaderMaterial && (h.current.material.uniforms.uTime.value = i); }, setModelA: j, setModelB: C }), [U, j, C] ), it((i) => { var b, M, T; const { gl: o } = i; if (o.setRenderTarget(X), o.clear(), o.render(z.current, K.current), o.setRenderTarget(null), ((b = h.current) == null ? void 0 : b.material) instanceof a.ShaderMaterial && (h.current.material.uniforms.uPosition.value = X.texture), ((M = g.current) == null ? void 0 : M.material) instanceof a.ShaderMaterial && ((T = h.current) == null ? void 0 : T.material) instanceof a.ShaderMaterial) { const I = g.current.material.uniforms.uModel1.value, q = g.current.material.uniforms.uModel2.value; h.current.material.uniforms.uModel1.value !== I && (h.current.material.uniforms.uModel1.value = I), h.current.material.uniforms.uModel2.value !== q && (h.current.material.uniforms.uModel2.value = q); } }), /* @__PURE__ */ B(tt, { children: [ ot( /* @__PURE__ */ B("mesh", { ref: g, children: [ /* @__PURE__ */ B("bufferGeometry", { children: [ /* @__PURE__ */ y( "bufferAttribute", { attach: "attributes-position", args: [O.positions, 3] } ), /* @__PURE__ */ y( "bufferAttribute", { attach: "attributes-uv", args: [O.uvs, 2] } ) ] }), /* @__PURE__ */ y( "shaderMaterial", { uniforms: Q, vertexShader: ht, fragmentShader: dt(x) } ) ] }), z.current ), /* @__PURE__ */ B("points", { ...N, ref: h, children: [ /* @__PURE__ */ B("bufferGeometry", { children: [ /* @__PURE__ */ y( "bufferAttribute", { attach: "attributes-position", args: [Z, 3] } ), l.map((i, o) => /* @__PURE__ */ y( "bufferAttribute", { attach: i.attach, args: [i.array, i.itemSize] }, o )) ] }), /* @__PURE__ */ y( "shaderMaterial", { uniforms: Y, vertexShader: bt(f, H), fragmentShader: pt(A), depthWrite: !1, blending: c, transparent: !0, vertexColors: !0 } ) ] }) ] }); } ); export { xt as R3FPointsFX, at as nextPerfectSquare };