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.

120 lines (94 loc) 11.5 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react/jsx-runtime"),rt=require("@react-three/drei"),_=require("@react-three/fiber"),it=require("react"),F=require("three");function H(o){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const r in o)if(r!=="default"){const e=Object.getOwnPropertyDescriptor(o,r);Object.defineProperty(t,r,e.get?e:{enumerable:!0,get:()=>o[r]})}}return t.default=o,Object.freeze(t)}const p=H(it),a=H(F),L=o=>{if(o<0)return 0;const t=Math.ceil(Math.sqrt(o));return t*t},d=new F.Triangle,j=new F.Vector3,G=new F.Vector2,W=new F.Vector2,$=new F.Vector2;class ot{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,r=this.positionAttribute,e=this.weightAttribute,u=t?t.count/3:r.count/3,v=new Float32Array(u);for(let l=0;l<u;l++){let c=1,f=3*l,A=3*l+1,y=3*l+2;t&&(f=t.getX(f),A=t.getX(A),y=t.getX(y)),e&&(c=e.getX(f)+e.getX(A)+e.getX(y)),d.a.fromBufferAttribute(r,f),d.b.fromBufferAttribute(r,A),d.c.fromBufferAttribute(r,y),c*=d.getArea(),v[l]=c}const i=new Float32Array(u);let m=0;for(let l=0;l<u;l++)m+=v[l],i[l]=m;return this.distribution=i,this}setRandomGenerator(t){return this.randomFunction=t,this}sample(t,r,e,u){const v=this.sampleFaceIndex();return this.sampleFace(v,t,r,e,u)}sampleFaceIndex(){const t=this.distribution[this.distribution.length-1];return this.binarySearch(this.randomFunction()*t)}binarySearch(t){const r=this.distribution;let e=0,u=r.length-1,v=-1;for(;e<=u;){const i=Math.ceil((e+u)/2);if(i===0||r[i-1]<=t&&r[i]>t){v=i;break}else t<r[i]?u=i-1:e=i+1}return v}sampleFace(t,r,e,u,v){let i=this.randomFunction(),m=this.randomFunction();i+m>1&&(i=1-i,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),r.set(0,0,0).addScaledVector(d.a,i).addScaledVector(d.b,m).addScaledVector(d.c,1-(i+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,i).addScaledVector(d.b,m).addScaledVector(d.c,1-(i+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),j.set(0,0,0).addScaledVector(d.a,i).addScaledVector(d.b,m).addScaledVector(d.c,1-(i+m)),u.r=j.x,u.g=j.y,u.b=j.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,i).addScaledVector(W,m).addScaledVector($,1-(i+m))),this}}const nt=(o,t)=>{const r=o*4,e=new Float32Array(r),u=new ot(t).build(),v=Math.sqrt(o),i=v;for(let l=0;l<o;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,i,a.RGBAFormat,a.FloatType);return m.needsUpdate=!0,m},st=(o,t)=>{const r=t.geometry.attributes.position,e=new Float32Array(o*4),u=Math.sqrt(o),v=u;let i=0;for(let l=0;l<o;l++){const c=l*4;if(i<r.count){const f=i*3;e[c]=r.array[f],e[c+1]=r.array[f+1],e[c+2]=r.array[f+2],e[c+3]=1,i++}else{const f=Math.floor(Math.random()*r.count);e[c]=r.array[f*3],e[c+1]=r.array[f*3+1],e[c+2]=r.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},at=(o,t,r=!1)=>r?st(o,t):nt(o,t),k=o=>Object.entries(o).reduce((e,[u,v])=>{const i=a.UniformsUtils.clone({[u]:{value:v}});return{...e,...i}},{}),ut=` float progressModifier(vec3 origin, vec3 target, float progress){ return progress; } `,lt=o=>` uniform float uTime; uniform float uTransitionProgress; uniform sampler2D positionsA; uniform sampler2D positionsB; uniform int uModel1; uniform int uModel2; varying vec2 vUv; ${o||ut} 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); } `,ct=` 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; } `,ft=` 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; } `,mt=o=>` uniform vec3 uColor; uniform float uTime; uniform int uModel1; uniform int uModel2; uniform float uAlpha; varying vec3 vPosition; varying float vTransitionProgress; ${o||ft} void main() { vec4 color1 = modifier(uModel1); vec4 color2 = modifier(uModel2); gl_FragColor = mix(color1, color2, vTransitionProgress); } `,dt=` VertexProperties modifier(vec3 pos, float progress){ VertexProperties result; result.position = pos; result.pointSize = uPointSize; result.progress = progress; return result; } `,ht=(o,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; }; ${o||dt} 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; } `,vt=p.forwardRef(({models:o,pointsCount:t=1e3,modelA:r=null,modelB:e=null,uniforms:u={},baseColor:v=new a.Color(0,0,0),pointSize:i=.1,alpha:m=1,attributes:l=[],blending:c=a.AdditiveBlending,vertexModifier:f,fragmentModifier:A,progressModifier:y,sizeAttenutation:N=!0,organizedParticleIndexes:J=[],...K},Q)=>{const g=p.useRef(null),h=p.useRef(null),w=p.useRef(r),T=p.useRef(e),O=p.useRef(new a.Scene),Y=p.useRef(new a.OrthographicCamera(-1,1,1,-1,1/Math.pow(2,53),1)),{gl:B,size:{width:D,height:V}}=_.useThree(),S=p.useMemo(()=>L(t),[t]),x=p.useMemo(()=>{const n=[];return o.forEach((s,b)=>{n.push(at(S,s,J.includes(b)))}),n},[o,S]),Z=p.useMemo(()=>k({uTransitionProgress:0,uTime:0,positionsA:w.current!==null?x[w.current]:null,positionsB:T.current!==null?x[T.current]:null,uModel1:w.current,uModel2:T.current}),[x]),tt=p.useMemo(()=>k({uPosition:null,uColor:v,uTime:0,uModel1:w.current,uModel2:T.current,uPointSize:i/10,uAlpha:m,uViewPort:new a.Vector2(D,V),uDpr:B.getPixelRatio(),...u}),[]),E=p.useCallback(n=>{var s;((s=g.current)==null?void 0:s.material)instanceof a.ShaderMaterial&&(g.current.material.uniforms.uTransitionProgress.value=Math.min(n,1))},[]),z=p.useCallback(n=>{var b;let s=null;n>=0&&n<x.length&&(s=n,w.current=s),((b=g.current)==null?void 0:b.material)instanceof a.ShaderMaterial&&(g.current.material.uniforms.positionsA.value=s!==null?x[s]:null,g.current.material.uniforms.uModel1.value=s)},[x]),q=p.useCallback(n=>{var b;let s=null;n>=0&&n<x.length&&(s=n,T.current=s),((b=g.current)==null?void 0:b.material)instanceof a.ShaderMaterial&&(g.current.material.uniforms.positionsB.value=s!==null?x[s]:null,g.current.material.uniforms.uModel2.value=s)},[x]);p.useEffect(()=>{var n;((n=h.current)==null?void 0:n.material)instanceof a.ShaderMaterial&&(h.current.material.uniforms.uColor.value=v,h.current.material.uniforms.uPointSize.value=i/10,h.current.material.uniforms.uAlpha.value=m),Object.entries(u).forEach(([s,b])=>{var P;((P=h.current)==null?void 0:P.material)instanceof a.ShaderMaterial&&h.current.material.uniforms[s]&&(h.current.material.uniforms[s].value=b)})},[v,i,m,u]),p.useEffect(()=>{var n;((n=h.current)==null?void 0:n.material)instanceof a.ShaderMaterial&&(h.current.material.uniforms.uViewPort.value.set(D,V),h.current.material.uniforms.uDpr.value=B.getPixelRatio())},[D,V,B]);const U=p.useMemo(()=>{const n=new Float32Array([-1,-1,0,1,-1,0,1,1,0,-1,-1,0,1,1,0,-1,1,0]),s=new Float32Array([0,1,1,1,1,0,0,1,1,0,0,0]);return{positions:n,uvs:s}},[]),C=rt.useFBO(Math.sqrt(S),Math.sqrt(S),{minFilter:a.NearestFilter,magFilter:a.NearestFilter,type:a.FloatType}),et=p.useMemo(()=>{const n=Math.sqrt(S),s=n,b=new Float32Array(S*3);for(let P=0;P<S;P++){const R=P*3;b[R+0]=P%s/s,b[R+1]=P/s/n}return b},[S]);return p.useImperativeHandle(Q,()=>({getSimulationMesh:()=>g.current,getPointsMesh:()=>h.current,updateProgress:E,updateTime:n=>{g.current&&g.current.material instanceof a.ShaderMaterial&&(g.current.material.uniforms.uTime.value=n),h.current&&h.current.material instanceof a.ShaderMaterial&&(h.current.material.uniforms.uTime.value=n)},setModelA:z,setModelB:q}),[E,z,q]),_.useFrame(n=>{var b,P,R;const{gl:s}=n;if(s.setRenderTarget(C),s.clear(),s.render(O.current,Y.current),s.setRenderTarget(null),((b=h.current)==null?void 0:b.material)instanceof a.ShaderMaterial&&(h.current.material.uniforms.uPosition.value=C.texture),((P=g.current)==null?void 0:P.material)instanceof a.ShaderMaterial&&((R=h.current)==null?void 0:R.material)instanceof a.ShaderMaterial){const X=g.current.material.uniforms.uModel1.value,I=g.current.material.uniforms.uModel2.value;h.current.material.uniforms.uModel1.value!==X&&(h.current.material.uniforms.uModel1.value=X),h.current.material.uniforms.uModel2.value!==I&&(h.current.material.uniforms.uModel2.value=I)}}),M.jsxs(M.Fragment,{children:[_.createPortal(M.jsxs("mesh",{ref:g,children:[M.jsxs("bufferGeometry",{children:[M.jsx("bufferAttribute",{attach:"attributes-position",args:[U.positions,3]}),M.jsx("bufferAttribute",{attach:"attributes-uv",args:[U.uvs,2]})]}),M.jsx("shaderMaterial",{uniforms:Z,vertexShader:ct,fragmentShader:lt(y)})]}),O.current),M.jsxs("points",{...K,ref:h,children:[M.jsxs("bufferGeometry",{children:[M.jsx("bufferAttribute",{attach:"attributes-position",args:[et,3]}),l.map((n,s)=>M.jsx("bufferAttribute",{attach:n.attach,args:[n.array,n.itemSize]},s))]}),M.jsx("shaderMaterial",{uniforms:tt,vertexShader:ht(f,N),fragmentShader:mt(A),depthWrite:!1,blending:c,transparent:!0,vertexColors:!0})]})]})});exports.R3FPointsFX=vt;exports.nextPerfectSquare=L;