foundry-component-library 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/components/Awards/index.tsx +37 -32
  2. package/lib/components/Awards/styles.module.scss +26 -1
  3. package/lib/components/ServiceHubsTeaserEffects/TileBalls.tsx +35 -0
  4. package/lib/components/ServiceHubsTeaserEffects/TileFluid.tsx +29 -0
  5. package/lib/components/ServiceHubsTeaserEffects/TileGlass.tsx +23 -0
  6. package/lib/components/ServiceHubsTeaserEffects/TileRays.tsx +94 -0
  7. package/lib/components/ServiceHubsTeaserEffects/bubbles/Bubble.tsx +35 -0
  8. package/lib/components/ServiceHubsTeaserEffects/bubbles/Bubbles.tsx +44 -0
  9. package/lib/components/ServiceHubsTeaserEffects/fluid/Fluid.tsx +192 -0
  10. package/lib/components/ServiceHubsTeaserEffects/fluid/constant.ts +23 -0
  11. package/lib/components/ServiceHubsTeaserEffects/fluid/effect/Fluid.tsx +22 -0
  12. package/lib/components/ServiceHubsTeaserEffects/fluid/effect/FluidEffect.tsx +58 -0
  13. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/advection.frag +16 -0
  14. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/base.vert +26 -0
  15. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/clear.frag +7 -0
  16. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/composite.frag +41 -0
  17. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/curl.frag +22 -0
  18. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/divergence.frag +41 -0
  19. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/gradientSubstract.frag +26 -0
  20. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/pressure.frag +28 -0
  21. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/splat.frag +19 -0
  22. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/vorticity.frag +36 -0
  23. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl.d.ts +14 -0
  24. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useDoubleFBO.tsx +37 -0
  25. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useFBOs.tsx +71 -0
  26. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useMaterials.tsx +242 -0
  27. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/usePointer.tsx +54 -0
  28. package/lib/components/ServiceHubsTeaserEffects/fluid/index.ts +5 -0
  29. package/lib/components/ServiceHubsTeaserEffects/fluid/types.ts +27 -0
  30. package/lib/components/ServiceHubsTeaserEffects/fluid/utils.ts +12 -0
  31. package/lib/components/ServiceHubsTeaserEffects/glass/Lens.tsx +66 -0
  32. package/lib/components/ServiceHubsTeaserEffects/index.tsx +67 -0
  33. package/lib/components/ServiceHubsTeaserEffects/rays/LightSource.tsx +64 -0
  34. package/lib/components/ServiceHubsTeaserEffects/rays/noise.js +97 -0
  35. package/lib/components/ServiceHubsTeaserEffects/styles.module.scss +135 -0
  36. package/lib/index.ts +2 -0
  37. package/lib/queries/getHomePage.ts +10 -0
  38. package/lib/queries/getPageBySlug.ts +39 -4
  39. package/package.json +6 -2
@@ -1,12 +1,25 @@
1
1
  "use client";
2
- import { useRef } from "react";
3
2
  import styles from "./styles.module.scss";
4
3
  import Container from "../Container";
5
4
  import { NextImage } from "../../types";
6
- import useDrag from "../../hooks/useDrag";
7
5
  import WavyText from "../TextAnimations/WavyText";
8
6
  import { motion } from "framer-motion";
9
7
 
8
+ // Infinite marquee animation settings
9
+ const marqueeVariants = {
10
+ animate: {
11
+ x: [0, -"100%"],
12
+ transition: {
13
+ x: {
14
+ repeat: Infinity,
15
+ repeatType: "loop",
16
+ duration: 20,
17
+ ease: "linear",
18
+ },
19
+ },
20
+ },
21
+ };
22
+
10
23
  const Awards = ({
11
24
  heading,
12
25
  awards,
@@ -22,49 +35,41 @@ const Awards = ({
22
35
  }[];
23
36
  Image: NextImage;
24
37
  }) => {
25
- const sectionRef = useRef(null);
26
- const { handleMouseDown, handleMouseMove, handleMouseUp, dragStyle } =
27
- useDrag(sectionRef);
38
+ if (!awards) return null;
28
39
 
29
- if (!awards) return;
40
+ // Duplicate awards to create seamless looping
41
+ const marqueeItems = [...awards, ...awards];
30
42
 
31
43
  return (
32
44
  <motion.div
33
45
  className={styles.awards}
34
46
  initial={{ opacity: 0, y: -5 }}
35
47
  animate={{ opacity: 1, y: 0 }}
36
- transition={{
37
- duration: 1,
38
- }}
39
- >
48
+ transition={{ duration: 1 }}>
40
49
  <Container>
41
50
  {heading && <WavyText className={styles.heading} text={heading} />}
42
51
  </Container>
43
52
  <Container noMobilePadding>
44
- <div
45
- ref={sectionRef}
46
- className={styles.items}
47
- onMouseDown={handleMouseDown}
48
- onMouseMove={handleMouseMove}
49
- onMouseUp={handleMouseUp}
50
- onMouseLeave={handleMouseUp}
51
- style={dragStyle as React.CSSProperties}
52
- >
53
- {awards.map((award, i) => {
54
- const { image, heading, text } = award;
55
-
56
- if (!image) return;
53
+ <div className={styles.marqueeWrapper}>
54
+ <motion.div
55
+ className={styles.marquee}
56
+ variants={marqueeVariants}
57
+ animate="animate">
58
+ {marqueeItems.map((award, i) => {
59
+ const { image, heading, text } = award;
60
+ if (!image) return null;
57
61
 
58
- return (
59
- <div key={image.sourceUrl + i} className={styles.award}>
60
- <div className={styles.image}>
61
- {image && <Image src={image?.sourceUrl} alt="" fill />}
62
+ return (
63
+ <div key={image.sourceUrl + i} className={styles.award}>
64
+ <div className={styles.image}>
65
+ {image && <Image src={image.sourceUrl} alt="" fill />}
66
+ </div>
67
+ {heading && <div className={styles.text}>{heading}</div>}
68
+ {text && <div className={styles.client}>{text}</div>}
62
69
  </div>
63
- {heading && <div className={styles.text}>{heading}</div>}
64
- {text && <div className={styles.client}>{text}</div>}
65
- </div>
66
- );
67
- })}
70
+ );
71
+ })}
72
+ </motion.div>
68
73
  </div>
69
74
  </Container>
70
75
  </motion.div>
@@ -21,14 +21,29 @@
21
21
  }
22
22
  }
23
23
 
24
- .items {
24
+ /* NEW — wrapper that clips the scrolling track */
25
+ .marqueeWrapper {
26
+ overflow: hidden;
27
+ width: 100%;
28
+ position: relative;
29
+ }
30
+
31
+ /* NEW — marquee track */
32
+ .marquee {
25
33
  display: flex;
34
+ gap: 48px;
35
+ width: max-content;
36
+ animation: marquee 20s linear infinite;
26
37
 
27
38
  @media #{$QUERY-sm} {
28
39
  gap: 24px;
40
+ animation-duration: 16s;
29
41
  }
30
42
  }
31
43
 
44
+ /* Remove if using marquee (track should scroll) */
45
+ /* .items { display: flex; } */
46
+
32
47
  .award {
33
48
  width: 20%;
34
49
  text-align: center;
@@ -85,3 +100,13 @@
85
100
  font-size: 16px;
86
101
  }
87
102
  }
103
+
104
+ /* NEW — marquee animation: scroll half the track (because items are duplicated) */
105
+ @keyframes marquee {
106
+ 0% {
107
+ transform: translateX(0);
108
+ }
109
+ 100% {
110
+ transform: translateX(-50%);
111
+ }
112
+ }
@@ -0,0 +1,35 @@
1
+ import { Canvas } from "@react-three/fiber";
2
+ import { Environment, Text } from "@react-three/drei";
3
+ import { EffectComposer, N8AO, TiltShift2 } from "@react-three/postprocessing";
4
+ import Bubbles from "./bubbles/Bubbles";
5
+
6
+ const TileBalls = () => {
7
+ return (
8
+ <Canvas
9
+ shadows
10
+ dpr={[1, 2]}
11
+ gl={{ antialias: false }}
12
+ camera={{ fov: 15, position: [0, 0, 20] }}>
13
+ <color attach="background" args={["#380de8"]} />
14
+ <fog attach="fog" args={["red", 20, -5]} />
15
+ <ambientLight intensity={1.5} />
16
+ <pointLight position={[10, 10, 10]} intensity={1} castShadow />
17
+ <Bubbles />
18
+ <EffectComposer>
19
+ <N8AO aoRadius={6} intensity={2} color="red" />
20
+ <TiltShift2 blur={0.1} />
21
+ </EffectComposer>
22
+ <Environment preset="city" />
23
+ <Text
24
+ position={[0, 0, 0]}
25
+ fontSize={0.4}
26
+ textAlign="center"
27
+ fontWeight={600}
28
+ color="#fff">
29
+ Content and{"\n"}Campaigning
30
+ </Text>
31
+ </Canvas>
32
+ );
33
+ };
34
+
35
+ export default TileBalls;
@@ -0,0 +1,29 @@
1
+ import { Text } from "@react-three/drei";
2
+ import { Canvas } from "@react-three/fiber";
3
+ import { EffectComposer } from "@react-three/postprocessing";
4
+ import { Fluid } from "./fluid/Fluid";
5
+
6
+ const TileFluid = () => {
7
+ return (
8
+ <Canvas
9
+ style={{
10
+ height: "100%",
11
+ width: "100%",
12
+ }}
13
+ camera={{ position: [0, 0, 20], fov: 15 }}>
14
+ <Text
15
+ position={[0, 0, 0]}
16
+ fontSize={0.4}
17
+ textAlign="center"
18
+ fontWeight={600}
19
+ color="#380de8">
20
+ Branding &{"\n"}Corporate ID
21
+ </Text>
22
+ <EffectComposer>
23
+ <Fluid backgroundColor="#fbff00" fluidColor="#380de8" rainbow={true} />
24
+ </EffectComposer>
25
+ </Canvas>
26
+ );
27
+ };
28
+
29
+ export default TileFluid;
@@ -0,0 +1,23 @@
1
+ import { Canvas } from "@react-three/fiber";
2
+ import { Text } from "@react-three/drei";
3
+ import Lens from "./glass/Lens";
4
+
5
+ const TileGlass = () => {
6
+ return (
7
+ <Canvas camera={{ position: [0, 0, 20], fov: 15 }}>
8
+ <Lens>
9
+ <Text
10
+ position={[0, 0, 0]}
11
+ fontSize={0.4}
12
+ textAlign="center"
13
+ fontWeight={600}
14
+ color="#491b11">
15
+ Strategy &{"\n"}
16
+ Positioning
17
+ </Text>
18
+ </Lens>
19
+ </Canvas>
20
+ );
21
+ };
22
+
23
+ export default TileGlass;
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import React, { useRef } from "react";
4
+ import { Canvas, useFrame, useThree } from "@react-three/fiber";
5
+ import { OrbitControls, useGLTF } from "@react-three/drei";
6
+ import { Vector2, MeshBasicMaterial, DoubleSide } from "three";
7
+ import { LightSource } from "./rays/LightSource";
8
+ import {
9
+ EffectComposer,
10
+ EffectPass,
11
+ GodRaysEffect,
12
+ RenderPass,
13
+ } from "postprocessing";
14
+
15
+ function Slab({ url }) {
16
+ const gltf = useGLTF(url);
17
+ const slabRef = useRef();
18
+ const { camera, scene, gl } = useThree();
19
+
20
+ // GodRays light
21
+ const lightRef = useRef();
22
+ useFrame(({ pointer }) => {
23
+ if (lightRef.current) {
24
+ const LIGHT_RANGE = 5;
25
+ lightRef.current.position.x = pointer.x * LIGHT_RANGE;
26
+ lightRef.current.position.y = pointer.y * LIGHT_RANGE;
27
+ }
28
+ });
29
+
30
+ React.useEffect(() => {
31
+ if (!gltf) return;
32
+
33
+ const slab = gltf.scene;
34
+ slab.rotation.x = Math.PI / 2;
35
+
36
+ slab.traverse((obj) => {
37
+ if (obj.isMesh) {
38
+ obj.material = new MeshBasicMaterial({
39
+ color: 0x491b11,
40
+ side: DoubleSide,
41
+ });
42
+ }
43
+ });
44
+
45
+ slabRef.current.add(slab);
46
+
47
+ // Setup postprocessing
48
+ const composer = new EffectComposer(gl);
49
+ composer.addPass(new RenderPass(scene, camera));
50
+
51
+ const gre = new GodRaysEffect(camera, lightRef.current, {
52
+ height: 480,
53
+ kernelSize: 2,
54
+ density: 1,
55
+ decay: 0.8,
56
+ weight: 0.5,
57
+ exposure: 0.3,
58
+ samples: 20,
59
+ clampMax: 0.95,
60
+ });
61
+
62
+ composer.addPass(new EffectPass(camera, gre));
63
+
64
+ // Render loop override
65
+ const originalSetAnimationLoop = gl.setAnimationLoop;
66
+ gl.setAnimationLoop(() => composer.render());
67
+
68
+ return () => {
69
+ // Cleanup
70
+ gl.setAnimationLoop(originalSetAnimationLoop);
71
+ composer.dispose();
72
+ };
73
+ }, [gltf, gl, scene, camera]);
74
+
75
+ return (
76
+ <>
77
+ {/* <mesh ref={lightRef} position={[0, 0, -10]} /> */}
78
+ <group ref={slabRef} />
79
+ <LightSource ref={lightRef} position={[0, 0, -20]} />
80
+ </>
81
+ );
82
+ }
83
+
84
+ export default function SlabScene() {
85
+ return (
86
+ <Canvas
87
+ camera={{ fov: 60, position: [0, 0, 10], near: 1, far: 100 }}
88
+ gl={{ antialias: true, powerPreference: "high-performance" }}>
89
+ {/* <ambientLight intensity={0.2} /> */}
90
+ {/* <OrbitControls /> */}
91
+ <Slab url="/slab-with-text.glb" />
92
+ </Canvas>
93
+ );
94
+ }
@@ -0,0 +1,35 @@
1
+ import { useRef } from "react";
2
+ import { useFrame } from "@react-three/fiber";
3
+ import { Instance } from "@react-three/drei";
4
+
5
+ const Bubble = ({ factor, speed, xFactor, yFactor, zFactor }) => {
6
+ const ref = useRef();
7
+
8
+ useFrame((state) => {
9
+ const t = factor + state.clock.elapsedTime * (speed / 2);
10
+
11
+ ref.current!.scale.setScalar(Math.max(1.5, Math.cos(t) * 5));
12
+
13
+ ref.current!.position.set(
14
+ Math.cos(t) +
15
+ Math.sin(t * 1) / 10 +
16
+ xFactor +
17
+ Math.cos((t / 10) * factor) +
18
+ (Math.sin(t * 1) * factor) / 10,
19
+ Math.sin(t) +
20
+ Math.cos(t * 2) / 10 +
21
+ yFactor +
22
+ Math.sin((t / 10) * factor) +
23
+ (Math.cos(t * 2) * factor) / 10,
24
+ Math.sin(t) +
25
+ Math.cos(t * 2) / 10 +
26
+ zFactor +
27
+ Math.cos((t / 10) * factor) +
28
+ (Math.sin(t * 3) * factor) / 4
29
+ );
30
+ });
31
+
32
+ return <Instance ref={ref} />;
33
+ };
34
+
35
+ export default Bubble;
@@ -0,0 +1,44 @@
1
+ import { MathUtils } from "three";
2
+ import { useRef } from "react";
3
+ import { useFrame } from "@react-three/fiber";
4
+ import { Instances } from "@react-three/drei";
5
+ import Bubble from "./Bubble";
6
+
7
+ const particles = Array.from({ length: 20 }, () => ({
8
+ factor: MathUtils.randInt(20, 100),
9
+ speed: MathUtils.randFloat(0.01, 0.75),
10
+ xFactor: MathUtils.randFloatSpread(3),
11
+ yFactor: MathUtils.randFloatSpread(3),
12
+ zFactor: MathUtils.randFloatSpread(5),
13
+ }));
14
+
15
+ const Bubbles = () => {
16
+ const ref = useRef(null);
17
+
18
+ useFrame(
19
+ (state, delta) =>
20
+ void (ref.current.rotation.y = MathUtils.damp(
21
+ ref.current.rotation.y,
22
+ (-state.pointer.x * Math.PI) / 6,
23
+ 2.75,
24
+ delta
25
+ ))
26
+ );
27
+
28
+ return (
29
+ <Instances
30
+ limit={particles.length}
31
+ ref={ref}
32
+ castShadow
33
+ receiveShadow
34
+ position={[0, 2.5, 0]}>
35
+ <sphereGeometry args={[0.45, 64, 64]} />
36
+ <meshStandardMaterial roughness={1} color="#f0f0f0" />
37
+ {particles.map((data, i) => (
38
+ <Bubble key={i} {...data} />
39
+ ))}
40
+ </Instances>
41
+ );
42
+ };
43
+
44
+ export default Bubbles;
@@ -0,0 +1,192 @@
1
+ import { createPortal, useFrame, useThree } from '@react-three/fiber';
2
+ import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import { Camera, Color, Mesh, Scene, Texture, Vector2, Vector3 } from 'three';
4
+ import { ShaderPass } from 'three/examples/jsm/Addons.js';
5
+ import { Effect as FluidEffect } from './effect/Fluid';
6
+ import { useFBOs } from './hooks/useFBOs';
7
+ import { useMaterials } from './hooks/useMaterials';
8
+ import { type FluidProps } from './types';
9
+ import { DEFAULT_CONFIG } from './constant';
10
+ import { usePointer } from './hooks/usePointer';
11
+ import { normalizeScreenHz } from './utils';
12
+
13
+ type MaterialName = keyof ReturnType<typeof useMaterials>;
14
+ type FBONames = keyof ReturnType<typeof useFBOs>;
15
+
16
+ type Uniforms = {
17
+ uColor: Vector3 | Color;
18
+ uPointer: Vector2;
19
+ uTarget: Texture | null;
20
+ uVelocity: Texture;
21
+ uCurl: Texture;
22
+ uTexture: Texture;
23
+ uPressure: Texture;
24
+ uDivergence: Texture;
25
+ uSource: Texture;
26
+ uRadius: number;
27
+ uClearValue: number;
28
+ uCurlValue: number;
29
+ uDissipation: number;
30
+ };
31
+
32
+ export const Fluid = ({
33
+ blend = DEFAULT_CONFIG.blend,
34
+ force = DEFAULT_CONFIG.force,
35
+ radius = DEFAULT_CONFIG.radius,
36
+ curl = DEFAULT_CONFIG.curl,
37
+ swirl = DEFAULT_CONFIG.swirl,
38
+ intensity = DEFAULT_CONFIG.intensity,
39
+ distortion = DEFAULT_CONFIG.distortion,
40
+ fluidColor = DEFAULT_CONFIG.fluidColor,
41
+ backgroundColor = DEFAULT_CONFIG.backgroundColor,
42
+ showBackground = DEFAULT_CONFIG.showBackground,
43
+ rainbow = DEFAULT_CONFIG.rainbow,
44
+ pressure = DEFAULT_CONFIG.pressure,
45
+ densityDissipation = DEFAULT_CONFIG.densityDissipation,
46
+ velocityDissipation = DEFAULT_CONFIG.velocityDissipation,
47
+ blendFunction = DEFAULT_CONFIG.blendFunction,
48
+ }: FluidProps) => {
49
+ const size = useThree((three) => three.size);
50
+ const gl = useThree((three) => three.gl);
51
+
52
+ const [bufferScene] = useState(() => new Scene());
53
+ const bufferCamera = useMemo(() => new Camera(), []);
54
+
55
+ const meshRef = useRef<Mesh>(null);
56
+ const postRef = useRef<ShaderPass>(null);
57
+ const pointerRef = useRef(new Vector2());
58
+ const colorRef = useRef(new Vector3());
59
+
60
+ const FBOs = useFBOs();
61
+ const materials = useMaterials();
62
+ const splatStack = usePointer({ force });
63
+
64
+ const setShaderMaterial = useCallback(
65
+ (name: MaterialName) => {
66
+ if (!meshRef.current) return;
67
+
68
+ meshRef.current.material = materials[name];
69
+ meshRef.current.material.needsUpdate = true;
70
+ },
71
+ [materials],
72
+ );
73
+
74
+ const setRenderTarget = useCallback(
75
+ (name: FBONames) => {
76
+ const target = FBOs[name];
77
+
78
+ if ('write' in target) {
79
+ gl.setRenderTarget(target.write);
80
+ gl.clear();
81
+ gl.render(bufferScene, bufferCamera);
82
+ target.swap();
83
+ } else {
84
+ gl.setRenderTarget(target);
85
+ gl.clear();
86
+ gl.render(bufferScene, bufferCamera);
87
+ }
88
+ },
89
+ [bufferCamera, bufferScene, FBOs, gl],
90
+ );
91
+
92
+ const setUniforms = useCallback(
93
+ <K extends keyof Uniforms>(material: MaterialName, uniform: K, value: Uniforms[K]) => {
94
+ const mat = materials[material];
95
+
96
+ if (mat && mat.uniforms[uniform]) {
97
+ mat.uniforms[uniform].value = value;
98
+ }
99
+ },
100
+ [materials],
101
+ );
102
+
103
+ useFrame((_, delta) => {
104
+ if (!meshRef.current || !postRef.current) return;
105
+
106
+ for (let i = splatStack.length - 1; i >= 0; i--) {
107
+ const { mouseX, mouseY, velocityX, velocityY } = splatStack[i];
108
+
109
+ pointerRef.current.set(mouseX, mouseY);
110
+ colorRef.current.set(velocityX, velocityY, 10.0);
111
+
112
+ setShaderMaterial('splat');
113
+ setUniforms('splat', 'uTarget', FBOs.velocity.read.texture);
114
+ setUniforms('splat', 'uPointer', pointerRef.current);
115
+ setUniforms('splat', 'uColor', colorRef.current);
116
+ setUniforms('splat', 'uRadius', radius / 100.0);
117
+ setRenderTarget('velocity');
118
+ setUniforms('splat', 'uTarget', FBOs.density.read.texture);
119
+ setRenderTarget('density');
120
+
121
+ splatStack.pop();
122
+ }
123
+
124
+ setShaderMaterial('curl');
125
+ setUniforms('curl', 'uVelocity', FBOs.velocity.read.texture);
126
+ setRenderTarget('curl');
127
+
128
+ setShaderMaterial('vorticity');
129
+ setUniforms('vorticity', 'uVelocity', FBOs.velocity.read.texture);
130
+ setUniforms('vorticity', 'uCurl', FBOs.curl.texture);
131
+ setUniforms('vorticity', 'uCurlValue', curl);
132
+ setRenderTarget('velocity');
133
+
134
+ setShaderMaterial('divergence');
135
+ setUniforms('divergence', 'uVelocity', FBOs.velocity.read.texture);
136
+ setRenderTarget('divergence');
137
+
138
+ setShaderMaterial('clear');
139
+ setUniforms('clear', 'uTexture', FBOs.pressure.read.texture);
140
+ setUniforms('clear', 'uClearValue', normalizeScreenHz(pressure, delta));
141
+ setRenderTarget('pressure');
142
+
143
+ setShaderMaterial('pressure');
144
+ setUniforms('pressure', 'uDivergence', FBOs.divergence.texture);
145
+
146
+ for (let i = 0; i < swirl; i++) {
147
+ setUniforms('pressure', 'uPressure', FBOs.pressure.read.texture);
148
+ setRenderTarget('pressure');
149
+ }
150
+
151
+ setShaderMaterial('gradientSubstract');
152
+ setUniforms('gradientSubstract', 'uPressure', FBOs.pressure.read.texture);
153
+ setUniforms('gradientSubstract', 'uVelocity', FBOs.velocity.read.texture);
154
+ setRenderTarget('velocity');
155
+
156
+ setShaderMaterial('advection');
157
+ setUniforms('advection', 'uVelocity', FBOs.velocity.read.texture);
158
+ setUniforms('advection', 'uSource', FBOs.velocity.read.texture);
159
+ setUniforms('advection', 'uDissipation', normalizeScreenHz(velocityDissipation, delta));
160
+
161
+ setRenderTarget('velocity');
162
+ setUniforms('advection', 'uVelocity', FBOs.velocity.read.texture);
163
+ setUniforms('advection', 'uSource', FBOs.density.read.texture);
164
+ setUniforms('advection', 'uDissipation', normalizeScreenHz(densityDissipation, delta));
165
+
166
+ setRenderTarget('density');
167
+ });
168
+
169
+ return (
170
+ <>
171
+ {createPortal(
172
+ <mesh ref={meshRef} scale={[size.width, size.height, 1]}>
173
+ <planeGeometry args={[2, 2]} />
174
+ </mesh>,
175
+ bufferScene,
176
+ )}
177
+
178
+ <FluidEffect
179
+ blendFunction={blendFunction}
180
+ intensity={intensity}
181
+ rainbow={rainbow}
182
+ distortion={distortion}
183
+ backgroundColor={backgroundColor}
184
+ blend={blend}
185
+ fluidColor={fluidColor}
186
+ showBackground={showBackground}
187
+ ref={postRef}
188
+ tFluid={FBOs.density.read.texture}
189
+ />
190
+ </>
191
+ );
192
+ };
@@ -0,0 +1,23 @@
1
+ import { BlendFunction } from 'postprocessing';
2
+
3
+ export const DEFAULT_CONFIG = {
4
+ blend: 5,
5
+ intensity: 2,
6
+ force: 1.1,
7
+ distortion: 0.4,
8
+ curl: 1.9,
9
+ radius: 0.3,
10
+ swirl: 4,
11
+ pressure: 0.8,
12
+ densityDissipation: 0.96,
13
+ velocityDissipation: 1.0,
14
+ fluidColor: '#3300ff',
15
+ backgroundColor: '#070410',
16
+ showBackground: true,
17
+ rainbow: false,
18
+ dyeRes: 512,
19
+ simRes: 128,
20
+ blendFunction: BlendFunction.SET,
21
+ } as const;
22
+
23
+ export const REFRESH_RATE = 60;
@@ -0,0 +1,22 @@
1
+ import { forwardRef, useEffect, useMemo } from 'react';
2
+ import { EffectProps } from '../types';
3
+ import { FluidEffect } from './FluidEffect';
4
+
5
+ export const Effect = forwardRef(function Fluid(props: EffectProps, ref) {
6
+ // prevent re-creating the effect on every render
7
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8
+ const effect = useMemo(() => new FluidEffect(props), []);
9
+
10
+ useEffect(() => {
11
+ effect.state = { ...props };
12
+ effect.update();
13
+ }, [effect, props]);
14
+
15
+ useEffect(() => {
16
+ return () => {
17
+ effect.dispose?.();
18
+ };
19
+ }, [effect]);
20
+
21
+ return <primitive ref={ref} object={effect} dispose={null} />;
22
+ });